[10041] Use for spell 49145 and ranks for decrease SPELL_DIRECT_DAMAGE damage.
[getmangos.git] / src / game / AchievementMgr.cpp
blob6517f8157b779fc60f35e37b9e1f3a2a54e1b88a
1 /*
2 * Copyright (C) 2005-2010 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 "Common.h"
20 #include "AchievementMgr.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 "Mail.h"
33 #include "GridNotifiersImpl.h"
34 #include "CellImpl.h"
35 #include "Language.h"
36 #include "MapManager.h"
37 #include "BattleGround.h"
38 #include "BattleGroundAB.h"
39 #include "Map.h"
40 #include "InstanceData.h"
42 #include "Policies/SingletonImp.h"
44 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
46 namespace MaNGOS
48 class AchievementChatBuilder
50 public:
51 AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id)
52 : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {}
53 void operator()(WorldPacket& data, int32 loc_idx)
55 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
57 data << uint8(i_msgtype);
58 data << uint32(LANG_UNIVERSAL);
59 data << uint64(i_player.GetGUID());
60 data << uint32(5);
61 data << uint64(i_player.GetGUID());
62 data << uint32(strlen(text)+1);
63 data << text;
64 data << uint8(0);
65 data << uint32(i_achievementId);
68 private:
69 Player const& i_player;
70 ChatMsg i_msgtype;
71 int32 i_textId;
72 uint32 i_achievementId;
74 } // namespace MaNGOS
77 bool AchievementCriteriaRequirement::IsValid(AchievementCriteriaEntry const* criteria)
79 if(requirementType >= MAX_ACHIEVEMENT_CRITERIA_REQUIREMENT_TYPE)
81 sLog.outErrorDb( "Table `achievement_criteria_requirement` for criteria (Entry: %u) have wrong requirement type (%u), ignore.", criteria->ID,requirementType);
82 return false;
85 switch(criteria->requiredType)
87 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
88 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
89 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
90 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list
91 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
92 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
93 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
94 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
95 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
96 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
97 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
98 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
99 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
100 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
101 break;
102 default:
103 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
104 return false;
107 switch(requirementType)
109 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
110 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
111 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
112 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
113 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
114 return true;
115 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
116 if (!creature.id || !ObjectMgr::GetCreatureTemplate(creature.id))
118 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.",
119 criteria->ID, criteria->requiredType,requirementType,creature.id);
120 return false;
122 return true;
123 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
124 if (!classRace.class_id && !classRace.race_id)
126 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.",
127 criteria->ID, criteria->requiredType,requirementType);
128 return false;
130 if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
132 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.",
133 criteria->ID, criteria->requiredType,requirementType,classRace.class_id);
134 return false;
136 if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
138 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.",
139 criteria->ID, criteria->requiredType,requirementType,classRace.race_id);
140 return false;
142 return true;
143 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
144 if (health.percent < 1 || health.percent > 100)
146 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.",
147 criteria->ID, criteria->requiredType,requirementType,health.percent);
148 return false;
150 return true;
151 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
152 if (player_dead.own_team_flag > 1)
154 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).",
155 criteria->ID, criteria->requiredType,requirementType,player_dead.own_team_flag);
156 return false;
158 return true;
159 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
160 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
162 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
163 if (!spellEntry)
165 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell id in value1 (%u), ignore.",
166 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.spell_id);
167 return false;
169 if (aura.effect_idx >= MAX_EFFECT_INDEX)
171 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell effect index in value2 (%u), ignore.",
172 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.effect_idx);
173 return false;
175 if (!spellEntry->EffectApplyAuraName[aura.effect_idx])
177 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
178 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);
179 return false;
181 return true;
183 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
184 if (!GetAreaEntryByAreaID(area.id))
186 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.",
187 criteria->ID, criteria->requiredType,requirementType,area.id);
188 return false;
190 return true;
191 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
192 if (level.minlevel > STRONG_MAX_LEVEL)
194 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.",
195 criteria->ID, criteria->requiredType,requirementType,level.minlevel);
196 return false;
198 return true;
199 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
200 if (gender.gender > GENDER_NONE)
202 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.",
203 criteria->ID, criteria->requiredType,requirementType,gender.gender);
204 return false;
206 return true;
207 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
208 if (difficulty.difficulty >= MAX_DIFFICULTY)
210 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.",
211 criteria->ID, criteria->requiredType,requirementType,difficulty.difficulty);
212 return false;
214 return true;
215 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
216 if (map_players.maxcount <= 0)
218 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.",
219 criteria->ID, criteria->requiredType,requirementType,map_players.maxcount);
220 return false;
222 return true;
223 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
224 if (team.team != ALLIANCE && team.team != HORDE)
226 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.",
227 criteria->ID, criteria->requiredType,requirementType,team.team);
228 return false;
230 return true;
231 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
232 if(drunk.state >= MAX_DRUNKEN)
234 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.",
235 criteria->ID, criteria->requiredType,requirementType,drunk.state);
236 return false;
238 return true;
239 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
240 if (!sHolidaysStore.LookupEntry(holiday.id))
242 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.",
243 criteria->ID, criteria->requiredType,requirementType,holiday.id);
244 return false;
246 return true;
247 case ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM_LVL:
248 if(equipped_item.item_quality >= MAX_ITEM_QUALITY)
250 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM (%u) have unknown quality state in value1 (%u), ignore.",
251 criteria->ID, criteria->requiredType,requirementType,equipped_item.item_quality);
252 return false;
254 return true;
255 default:
256 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,requirementType);
257 return false;
259 return false;
262 bool AchievementCriteriaRequirement::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
264 switch(requirementType)
266 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
267 return true;
268 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
269 if (!target || target->GetTypeId()!=TYPEID_UNIT)
270 return false;
271 return target->GetEntry() == creature.id;
272 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
273 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
274 return false;
275 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
276 return false;
277 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
278 return false;
279 return true;
280 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
281 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
282 return false;
283 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
284 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
285 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
286 return false;
287 // flag set == must be same team, not set == different team
288 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
289 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
290 return source->HasAura(aura.spell_id,SpellEffectIndex(aura.effect_idx));
291 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
293 uint32 zone_id,area_id;
294 source->GetZoneAndAreaId(zone_id,area_id);
295 return area.id==zone_id || area.id==area_id;
297 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
298 return target && target->HasAura(aura.spell_id,SpellEffectIndex(aura.effect_idx));
299 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
300 return miscvalue1 >= value.minvalue;
301 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
302 if (!target)
303 return false;
304 return target->getLevel() >= level.minlevel;
305 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
306 if (!target)
307 return false;
308 return target->getGender() == gender.gender;
309 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
310 return false; // always fail
311 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
312 return source->GetMap()->GetSpawnMode()==difficulty.difficulty;
313 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
314 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
315 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
316 if (!target || target->GetTypeId() != TYPEID_PLAYER)
317 return false;
318 return ((Player*)target)->GetTeam() == team.team;
319 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
320 return (uint32)Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state;
321 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
322 return IsHolidayActive(HolidayIds(holiday.id));
323 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
325 BattleGround* bg = source->GetBattleGround();
326 if(!bg)
327 return false;
328 return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score);
330 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
332 if (!source->IsInWorld())
333 return false;
334 Map* map = source->GetMap();
335 // BattleGroundMap-class is instanceable, but no InstanceMap-class
336 if (map->IsBattleGroundOrArena())
337 return false;
338 if (!map->Instanceable())
340 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-instance map %u",
341 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
342 return false;
344 InstanceData* data = ((InstanceMap*)map)->GetInstanceData();
345 if (!data)
347 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map not have instance script",
348 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
349 return false;
351 return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1);
353 case ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM_LVL:
355 Item* item = source->GetItemByPos(INVENTORY_SLOT_BAG_0,miscvalue1);
356 if (!item)
357 return false;
358 ItemPrototype const* proto = item->GetProto();
359 return proto->ItemLevel >= equipped_item.item_level && proto->Quality >= equipped_item.item_quality;
362 return false;
365 bool AchievementCriteriaRequirementSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
367 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
368 if(!itr->Meets(criteria_id, source, target, miscvalue))
369 return false;
371 return true;
374 AchievementMgr::AchievementMgr(Player *player)
376 m_player = player;
379 AchievementMgr::~AchievementMgr()
383 void AchievementMgr::Reset()
385 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
387 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
388 data << uint32(iter->first);
389 m_player->SendDirectMessage(&data);
392 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
394 WorldPacket data(SMSG_CRITERIA_DELETED,4);
395 data << uint32(iter->first);
396 m_player->SendDirectMessage(&data);
399 m_completedAchievements.clear();
400 m_criteriaProgress.clear();
401 DeleteFromDB(m_player->GetGUIDLow());
403 // re-fill data
404 CheckAllAchievementCriteria();
407 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
409 DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
411 if (!sWorld.getConfig(CONFIG_BOOL_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
412 return;
414 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
415 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
417 AchievementCriteriaEntry const *achievementCriteria = (*i);
419 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
420 if (!achievement)
421 continue;
423 // don't update already completed criteria
424 if (IsCompletedCriteria(achievementCriteria,achievement))
425 continue;
427 switch (type)
429 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
430 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
431 if (achievementCriteria->healing_done.flag == miscvalue1 &&
432 achievementCriteria->healing_done.mapid == miscvalue2)
433 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
434 break;
435 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
436 // reset only the criteria having the miscvalue1 condition
437 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
438 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
439 break;
440 default: // reset all cases
441 break;
446 void AchievementMgr::DeleteFromDB(uint32 lowguid)
448 CharacterDatabase.BeginTransaction ();
449 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
450 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
451 CharacterDatabase.CommitTransaction ();
454 void AchievementMgr::SaveToDB()
456 if(!m_completedAchievements.empty())
458 bool need_execute = false;
459 std::ostringstream ssdel;
460 std::ostringstream ssins;
461 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
463 if(!iter->second.changed)
464 continue;
466 /// first new/changed record prefix
467 if(!need_execute)
469 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
470 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
471 need_execute = true;
473 /// next new/changed record prefix
474 else
476 ssdel << ", ";
477 ssins << ", ";
480 // new/changed record data
481 ssdel << iter->first;
482 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
484 /// mark as saved in db
485 iter->second.changed = false;
488 if(need_execute)
489 ssdel << ")";
491 if(need_execute)
493 CharacterDatabase.Execute( ssdel.str().c_str() );
494 CharacterDatabase.Execute( ssins.str().c_str() );
498 if(!m_criteriaProgress.empty())
500 /// prepare deleting and insert
501 bool need_execute_del = false;
502 bool need_execute_ins = false;
503 std::ostringstream ssdel;
504 std::ostringstream ssins;
505 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
507 if(!iter->second.changed)
508 continue;
510 // deleted data (including 0 progress state)
512 /// first new/changed record prefix (for any counter value)
513 if(!need_execute_del)
515 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
516 need_execute_del = true;
518 /// next new/changed record prefix
519 else
520 ssdel << ", ";
522 // new/changed record data
523 ssdel << iter->first;
526 // store data only for real progress
527 if(iter->second.counter != 0)
529 /// first new/changed record prefix
530 if(!need_execute_ins)
532 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
533 need_execute_ins = true;
535 /// next new/changed record prefix
536 else
537 ssins << ", ";
539 // new/changed record data
540 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
543 /// mark as updated in db
544 iter->second.changed = false;
547 if(need_execute_del) // DELETE ... IN (.... _)_
548 ssdel << ")";
550 if(need_execute_del || need_execute_ins)
552 if(need_execute_del)
553 CharacterDatabase.Execute( ssdel.str().c_str() );
554 if(need_execute_ins)
555 CharacterDatabase.Execute( ssins.str().c_str() );
560 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
562 if(achievementResult)
566 Field *fields = achievementResult->Fetch();
568 uint32 achievement_id = fields[0].GetUInt32();
570 // don't must happen: cleanup at server startup in sAchievementMgr.LoadCompletedAchievements()
571 if(!sAchievementStore.LookupEntry(achievement_id))
572 continue;
574 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
575 ca.date = time_t(fields[1].GetUInt64());
576 ca.changed = false;
577 } while(achievementResult->NextRow());
578 delete achievementResult;
581 if(criteriaResult)
585 Field *fields = criteriaResult->Fetch();
587 uint32 id = fields[0].GetUInt32();
588 uint32 counter = fields[1].GetUInt32();
589 time_t date = time_t(fields[2].GetUInt64());
591 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
592 if (!criteria)
594 // we will remove not existed criteria for all characters
595 sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id);
596 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
597 continue;
600 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
601 continue;
603 CriteriaProgress& progress = m_criteriaProgress[id];
604 progress.counter = counter;
605 progress.date = date;
606 progress.changed = false;
607 } while(criteriaResult->NextRow());
608 delete criteriaResult;
613 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
615 if(GetPlayer()->GetSession()->PlayerLoading())
616 return;
618 DEBUG_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
620 if(Guild* guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId()))
622 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
623 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
624 guild->BroadcastWorker(say_do,GetPlayer());
627 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
629 // broadcast realm first reached
630 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
631 data << GetPlayer()->GetName();
632 data << uint64(GetPlayer()->GetGUID());
633 data << uint32(achievement->ID);
634 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
635 sWorld.SendGlobalMessage(&data);
637 // if player is in world he can tell his friends about new achievement
638 else if (GetPlayer()->IsInWorld())
640 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
641 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
642 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY),say_do);
644 Cell::VisitWorldObjects(GetPlayer(), say_worker, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY));
647 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
648 data << GetPlayer()->GetPackGUID();
649 data << uint32(achievement->ID);
650 data << uint32(secsToTimeBitFields(time(NULL)));
651 data << uint32(0);
652 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY), true);
655 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
657 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
658 data << uint32(id);
660 // the counter is packed like a packed Guid
661 data.appendPackGUID(progress->counter);
663 data << GetPlayer()->GetPackGUID();
664 data << uint32(0);
665 data << uint32(secsToTimeBitFields(progress->date));
666 data << uint32(0); // timer 1
667 data << uint32(0); // timer 2
668 GetPlayer()->SendDirectMessage(&data);
672 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
674 void AchievementMgr::CheckAllAchievementCriteria()
676 // suppress sending packets
677 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
678 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
681 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
682 static const uint32 achievIdForDangeon[][4] =
684 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
685 { 321, true, true, true },
686 { 916, false, true, false },
687 { 917, false, true, false },
688 { 918, true, false, false },
689 { 2219, false, false, true },
690 { 0, false, false, false }
694 * this function will be called whenever the user might have done a criteria relevant action
696 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
698 DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
700 if (!sWorld.getConfig(CONFIG_BOOL_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
701 return;
703 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
704 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
706 AchievementCriteriaEntry const *achievementCriteria = (*i);
708 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
709 continue;
711 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
712 if (!achievement)
713 continue;
715 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
716 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
717 continue;
719 // don't update already completed criteria
720 if (IsCompletedCriteria(achievementCriteria,achievement))
721 continue;
723 switch (type)
725 // std. case: increment at 1
726 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
727 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
728 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
729 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
730 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */
731 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
732 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
733 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
734 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
735 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
736 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
737 if(!miscvalue1)
738 continue;
739 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
740 break;
741 // std case: increment at miscvalue1
742 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
743 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
744 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
745 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
746 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
747 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
748 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
749 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */
750 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
751 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
752 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
753 if(!miscvalue1)
754 continue;
755 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
756 break;
757 // std case: high value at miscvalue1
758 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
759 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
760 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
761 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
762 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
763 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
764 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
765 if(!miscvalue1)
766 continue;
767 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
768 break;
770 // specialized cases
772 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
774 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
775 if (!miscvalue1)
776 continue;
777 if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
778 continue;
780 if (achievementCriteria->win_bg.additionalRequirement1_type || achievementCriteria->win_bg.additionalRequirement2_type)
782 // those requirements couldn't be found in the dbc
783 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
784 if (!data || !data->Meets(GetPlayer(),unit))
785 continue;
787 // some hardcoded requirements
788 else
790 BattleGround* bg = GetPlayer()->GetBattleGround();
791 if (!bg)
792 continue;
794 switch(achievementCriteria->referredAchievement)
796 case 161: // AB, Overcome a 500 resource disadvantage
798 if (bg->GetTypeID() != BATTLEGROUND_AB)
799 continue;
800 if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
801 continue;
802 break;
804 case 156: // AB, win while controlling all 5 flags (all nodes)
805 case 784: // EY, win while holding 4 bases (all nodes)
807 if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam()))
808 continue;
809 break;
811 case 1762: // SA, win without losing any siege vehicles
812 case 2192: // SA, win without losing any siege vehicles
813 continue; // not implemented
817 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
818 break;
820 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
822 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
823 if(!miscvalue1)
824 continue;
825 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
826 continue;
828 // those requirements couldn't be found in the dbc
829 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
830 if(!data || !data->Meets(GetPlayer(),unit))
831 continue;
833 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
834 break;
836 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
837 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
838 break;
839 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
840 // update at loading or specific skill update
841 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
842 continue;
843 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
844 SetCriteriaProgress(achievementCriteria, skillvalue);
845 break;
846 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
847 // update at loading or specific skill update
848 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
849 continue;
850 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
851 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
852 break;
853 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
854 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
855 SetCriteriaProgress(achievementCriteria, 1);
856 break;
857 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
859 uint32 counter =0;
860 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
861 if(itr->second.m_rewarded)
862 counter++;
863 SetCriteriaProgress(achievementCriteria, counter);
864 break;
866 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
868 // speedup for non-login case
869 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
870 continue;
872 uint32 counter =0;
873 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
875 Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first);
876 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
877 counter++;
879 SetCriteriaProgress(achievementCriteria, counter);
880 break;
882 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
883 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
884 if(!miscvalue1)
885 continue;
886 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
887 continue;
888 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
889 break;
890 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
891 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
892 if(!miscvalue1)
893 continue;
894 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
895 continue;
896 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
897 break;
898 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
900 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
901 if(!miscvalue1)
902 continue;
903 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
904 bool notfit = false;
905 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
907 if(achievIdByArenaSlot[j] == achievement->ID)
909 BattleGround* bg = GetPlayer()->GetBattleGround();
910 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
911 notfit = true;
913 break;
916 if(notfit)
917 continue;
919 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
920 break;
922 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
924 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
925 if(!miscvalue1)
926 continue;
928 Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr.FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
929 if(!map || !map->IsDungeon())
930 continue;
932 // search case
933 bool found = false;
934 for(int j = 0; achievIdForDangeon[j][0]; ++j)
936 if(achievIdForDangeon[j][0] == achievement->ID)
938 if(map->IsRaid())
940 // if raid accepted (ignore difficulty)
941 if(!achievIdForDangeon[j][2])
942 break; // for
944 else if(GetPlayer()->GetDungeonDifficulty()==DUNGEON_DIFFICULTY_NORMAL)
946 // dungeon in normal mode accepted
947 if(!achievIdForDangeon[j][1])
948 break; // for
950 else
952 // dungeon in heroic mode accepted
953 if(!achievIdForDangeon[j][3])
954 break; // for
957 found = true;
958 break; // for
961 if(!found)
962 continue;
964 //FIXME: work only for instances where max==min for players
965 if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
966 continue;
967 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
968 break;
971 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
972 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
973 if(!miscvalue1)
974 continue;
975 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
976 continue;
977 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
978 break;
979 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
980 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
981 if(!miscvalue1)
982 continue;
984 // if team check required: must kill by opposition faction
985 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
986 continue;
988 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
989 break;
990 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
992 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
993 if(!miscvalue1)
994 continue;
996 // those requirements couldn't be found in the dbc
997 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
998 if(!data || !data->Meets(GetPlayer(),unit))
999 continue;
1001 // miscvalue1 is the ingame fallheight*100 as stored in dbc
1002 SetCriteriaProgress(achievementCriteria, miscvalue1);
1003 break;
1005 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1006 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1007 if(!miscvalue1)
1008 continue;
1009 if(miscvalue2 != achievementCriteria->death_from.type)
1010 continue;
1011 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1012 break;
1013 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1015 // if miscvalues != 0, it contains the questID.
1016 if (miscvalue1)
1018 if (miscvalue1 != achievementCriteria->complete_quest.questID)
1019 continue;
1021 else
1023 // login case.
1024 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
1025 continue;
1028 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1029 switch(achievement->ID)
1031 case 31:
1032 case 1275:
1033 case 1276:
1034 case 1277:
1035 case 1282:
1036 case 1789:
1038 // those requirements couldn't be found in the dbc
1039 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1040 if(!data || !data->Meets(GetPlayer(),unit))
1041 continue;
1042 break;
1044 default:
1045 break;
1049 SetCriteriaProgress(achievementCriteria, 1);
1050 break;
1052 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1053 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1055 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
1056 continue;
1058 // those requirements couldn't be found in the dbc
1059 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1060 if(!data)
1061 continue;
1063 if(!data->Meets(GetPlayer(),unit))
1064 continue;
1066 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1067 break;
1069 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1070 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1072 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1073 continue;
1075 // those requirements couldn't be found in the dbc
1076 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1077 if(!data)
1078 continue;
1080 if(!data->Meets(GetPlayer(),unit))
1081 continue;
1083 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1084 break;
1086 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1087 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
1088 continue;
1090 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
1091 SetCriteriaProgress(achievementCriteria, 1);
1092 break;
1093 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1095 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
1096 // miscvalue2=count of item loot
1097 if (!miscvalue1 || !miscvalue2)
1098 continue;
1099 if (miscvalue1 != achievementCriteria->loot_type.lootType)
1100 continue;
1102 // zone specific
1103 if(achievementCriteria->loot_type.lootTypeCount==1)
1105 // those requirements couldn't be found in the dbc
1106 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1107 if(!data || !data->Meets(GetPlayer(),unit))
1108 continue;
1111 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1112 break;
1114 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1115 // speedup for non-login case
1116 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
1117 continue;
1118 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
1119 break;
1120 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1121 // miscvalue1 contains the personal rating
1122 if (!miscvalue1) // no update at login
1123 continue;
1125 // additional requirements
1126 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1128 // those requirements couldn't be found in the dbc
1129 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1130 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
1132 // reset the progress as we have a win without the requirement.
1133 SetCriteriaProgress(achievementCriteria, 0);
1134 continue;
1138 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1139 break;
1140 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1141 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1142 if(!miscvalue1)
1143 continue;
1144 if(achievementCriteria->use_item.itemID != miscvalue1)
1145 continue;
1146 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1147 break;
1148 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1149 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1150 if(!miscvalue1)
1151 continue;
1152 if(miscvalue1 != achievementCriteria->own_item.itemID)
1153 continue;
1154 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1155 break;
1156 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1158 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
1159 if(!worldOverlayEntry)
1160 break;
1162 bool matchFound = false;
1163 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
1165 uint32 area_id = worldOverlayEntry->areatableID[j];
1166 if(!area_id) // array have 0 only in empty tail
1167 break;
1169 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
1170 if(exploreFlag < 0)
1171 continue;
1173 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
1174 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1176 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1178 matchFound = true;
1179 break;
1183 if(matchFound)
1184 SetCriteriaProgress(achievementCriteria, 1);
1185 break;
1187 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1188 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1189 break;
1190 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1192 // skip faction check only at loading
1193 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1194 continue;
1196 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1197 if (reputation > 0)
1198 SetCriteriaProgress(achievementCriteria, reputation);
1199 break;
1201 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1203 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1204 break;
1206 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1208 // skip for login case
1209 if(!miscvalue1)
1210 continue;
1211 SetCriteriaProgress(achievementCriteria, 1);
1212 break;
1214 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1216 // miscvalue1 = equip_slot+1 (for avoid use 0)
1217 if(!miscvalue1)
1218 continue;
1219 uint32 item_slot = miscvalue1-1;
1220 if(item_slot != achievementCriteria->equip_epic_item.itemSlot)
1221 continue;
1222 // those requirements couldn't be found in the dbc
1223 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1224 if(!data || !data->Meets(GetPlayer(),unit,item_slot))
1225 continue;
1226 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_SET);
1227 break;
1229 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1230 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1232 // miscvalue1 = itemid
1233 // miscvalue2 = diced value
1234 if(!miscvalue1)
1235 continue;
1236 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1237 continue;
1238 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( miscvalue1 );
1240 uint32 requiredItemLevel = 0;
1241 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1242 requiredItemLevel = 185;
1244 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1245 continue;
1246 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1247 break;
1249 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1251 // miscvalue1 = emote
1252 if(!miscvalue1)
1253 continue;
1254 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1255 continue;
1256 if(achievementCriteria->do_emote.count)
1258 // those requirements couldn't be found in the dbc
1259 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1260 if(!data || !data->Meets(GetPlayer(),unit))
1261 continue;
1264 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1265 break;
1267 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1268 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1270 if (!miscvalue1)
1271 continue;
1273 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1275 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1276 continue;
1278 // map specific case (BG in fact) expected player targeted damage/heal
1279 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1280 continue;
1283 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1284 break;
1286 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1287 // miscvalue1 = item_id
1288 if (!miscvalue1)
1289 continue;
1290 if (miscvalue1 != achievementCriteria->equip_item.itemID)
1291 continue;
1293 SetCriteriaProgress(achievementCriteria, 1);
1294 break;
1295 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1296 // miscvalue1 = go entry
1297 if (!miscvalue1)
1298 continue;
1299 if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1300 continue;
1302 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1303 break;
1304 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1306 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1307 if (!miscvalue1)
1308 continue;
1310 // those requirements couldn't be found in the dbc
1311 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1312 if (!data)
1313 continue;
1315 if (!data->Meets(GetPlayer(),unit))
1316 continue;
1318 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1319 break;
1321 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1322 if (!miscvalue1)
1323 continue;
1324 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1325 continue;
1327 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1328 break;
1329 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1331 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1332 continue;
1334 uint32 spellCount = 0;
1335 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1336 spellIter != GetPlayer()->GetSpellMap().end();
1337 ++spellIter)
1339 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1340 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1342 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1343 spellCount++;
1346 SetCriteriaProgress(achievementCriteria, spellCount);
1347 break;
1349 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1350 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1351 if (!miscvalue1)
1352 continue;
1354 if (achievementCriteria->win_duel.duelCount)
1356 // those requirements couldn't be found in the dbc
1357 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1358 if (!data)
1359 continue;
1361 if (!data->Meets(GetPlayer(),unit))
1362 continue;
1365 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1366 break;
1367 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1368 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1369 break;
1370 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1371 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1372 break;
1373 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1374 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1375 break;
1376 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1377 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1379 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1380 if (!miscvalue1)
1381 continue;
1382 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1);
1383 if (!proto || proto->Quality < ITEM_QUALITY_EPIC)
1384 continue;
1385 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1386 break;
1388 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1390 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1391 continue;
1393 uint32 spellCount = 0;
1394 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1395 spellIter != GetPlayer()->GetSpellMap().end();
1396 ++spellIter)
1398 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1399 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1400 if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1401 spellCount++;
1403 SetCriteriaProgress(achievementCriteria, spellCount);
1404 break;
1406 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1407 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1408 break;
1409 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1410 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1411 continue;
1413 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1414 break;
1415 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1416 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1417 continue;
1419 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1420 break;
1421 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1422 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1423 break;
1424 // std case: not exist in DBC, not triggered in code as result
1425 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1426 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1427 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1428 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1429 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1430 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1431 break;
1432 // FIXME: not triggered in code as result, need to implement
1433 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1434 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1435 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1436 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1437 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1438 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1439 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1440 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1441 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1442 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1443 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1444 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1445 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1446 case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS:
1447 case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
1448 break; // Not implemented yet :(
1450 if(IsCompletedCriteria(achievementCriteria,achievement))
1451 CompletedCriteriaFor(achievement);
1453 // check again the completeness for SUMM and REQ COUNT achievements,
1454 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1455 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1457 if (IsCompletedAchievement(achievement))
1458 CompletedAchievement(achievement);
1461 if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
1463 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1464 if(IsCompletedAchievement(*itr))
1465 CompletedAchievement(*itr);
1470 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1471 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1473 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1475 // counter can never complete
1476 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1477 return false;
1479 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1481 // someone on this realm has already completed that achievement
1482 if(sAchievementMgr.IsRealmCompleted(achievement))
1483 return false;
1486 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1487 if(itr == m_criteriaProgress.end())
1488 return false;
1490 CriteriaProgress const* progress = &itr->second;
1492 switch(achievementCriteria->requiredType)
1494 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1495 return progress->counter >= achievementCriteria->win_bg.winCount;
1496 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1497 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1498 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1500 // skip wrong class achievements
1501 for(int i = 1; i < MAX_CLASSES; ++i)
1502 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1503 return false;
1505 // skip wrong race achievements
1506 for(int i = 1; i < MAX_RACES; ++i)
1507 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1508 return false;
1510 // appropriate class/race or not class/race specific
1511 return progress->counter >= achievementCriteria->reach_level.level;
1513 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1514 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1515 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1516 return progress->counter >= 1;
1517 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1518 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1519 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1520 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1521 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1522 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1523 return progress->counter >= achievementCriteria->healing_done.count;
1524 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1525 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1526 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1527 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1528 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1529 return progress->counter >= 1;
1530 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1531 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1532 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1533 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1534 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1535 return progress->counter >= achievementCriteria->cast_spell.castCount;
1536 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1537 return progress->counter >= 1;
1538 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1539 return progress->counter >= achievementCriteria->own_item.itemCount;
1540 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1541 return progress->counter >= achievementCriteria->win_rated_arena.count;
1542 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1543 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1544 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1545 return progress->counter >= achievementCriteria->use_item.itemCount;
1546 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1547 return progress->counter >= achievementCriteria->loot_item.itemCount;
1548 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1549 return progress->counter >= 1;
1550 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1551 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1552 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1553 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1554 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1555 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1556 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1557 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1558 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1559 return progress->counter >= achievementCriteria->equip_epic_item.count;
1560 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1561 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1562 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1563 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1564 return progress->counter >= achievementCriteria->hk_class.count;
1565 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1566 return progress->counter >= achievementCriteria->hk_race.count;
1567 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1568 return progress->counter >= achievementCriteria->do_emote.count;
1569 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1570 return progress->counter >= achievementCriteria->equip_item.count;
1571 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1572 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1573 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1574 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1575 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1576 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1577 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1578 return progress->counter >= achievementCriteria->special_pvp_kill.killCount;
1579 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1580 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1581 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1582 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1583 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1584 return progress->counter >= achievementCriteria->win_duel.duelCount;
1585 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1586 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1587 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1588 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1589 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1590 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1592 // handle all statistic-only criteria here
1593 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1594 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1595 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1596 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1597 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1598 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1599 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1600 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1601 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1602 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1603 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1604 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1605 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1606 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1607 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1608 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1609 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1610 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1611 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1612 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1613 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1614 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1615 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1616 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1617 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1618 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1619 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1620 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1621 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1622 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1623 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1624 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1625 return false;
1627 return false;
1630 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1632 // counter can never complete
1633 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1634 return;
1636 // already completed and stored
1637 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1638 return;
1640 if (IsCompletedAchievement(achievement))
1641 CompletedAchievement(achievement);
1644 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1646 // counter can never complete
1647 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1648 return false;
1650 // for achievement with referenced achievement criterias get from referenced and counter from self
1651 uint32 achievementForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1652 uint32 achievementForTestCount = entry->count;
1654 AchievementCriteriaEntryList const* cList = sAchievementMgr.GetAchievementCriteriaByAchievement(achievementForTestId);
1655 if(!cList)
1656 return false;
1657 uint32 count = 0;
1659 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1660 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1661 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1663 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1665 AchievementCriteriaEntry const* criteria = *itr;
1667 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1668 if(itrProgress == m_criteriaProgress.end())
1669 continue;
1671 CriteriaProgress const* progress = &itrProgress->second;
1672 count += progress->counter;
1674 // for counters, field4 contains the main count requirement
1675 if (count >= criteria->raw.count)
1676 return true;
1678 return false;
1681 // Default case - need complete all or
1682 bool completed_all = true;
1683 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1685 AchievementCriteriaEntry const* criteria = *itr;
1687 bool completed = IsCompletedCriteria(criteria,entry);
1689 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1690 if(completed)
1691 ++count;
1692 else
1693 completed_all = false;
1695 // completed as have req. count of completed criterias
1696 if(achievementForTestCount > 0 && achievementForTestCount <= count)
1697 return true;
1700 // all criterias completed requirement
1701 if(completed_all && achievementForTestCount==0)
1702 return true;
1704 return false;
1707 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1709 DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1711 CriteriaProgress *progress = NULL;
1713 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1715 if(iter == m_criteriaProgress.end())
1717 // not create record for 0 counter
1718 if(changeValue == 0)
1719 return;
1721 progress = &m_criteriaProgress[entry->ID];
1722 progress->counter = changeValue;
1723 progress->date = time(NULL);
1725 else
1727 progress = &iter->second;
1729 uint32 newValue = 0;
1730 switch(ptype)
1732 case PROGRESS_SET:
1733 newValue = changeValue;
1734 break;
1735 case PROGRESS_ACCUMULATE:
1737 // avoid overflow
1738 uint32 max_value = std::numeric_limits<uint32>::max();
1739 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1740 break;
1742 case PROGRESS_HIGHEST:
1743 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1744 break;
1747 // not update (not mark as changed) if counter will have same value
1748 if(progress->counter == newValue)
1749 return;
1751 progress->counter = newValue;
1754 progress->changed = true;
1756 if(entry->timeLimit)
1758 time_t now = time(NULL);
1759 if(time_t(progress->date + entry->timeLimit) < now)
1760 progress->counter = 1;
1762 // also it seems illogical, the timeframe will be extended at every criteria update
1763 progress->date = now;
1765 SendCriteriaUpdate(entry->ID,progress);
1768 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1770 DETAIL_LOG("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1771 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1772 return;
1774 SendAchievementEarned(achievement);
1775 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1776 ca.date = time(NULL);
1777 ca.changed = true;
1779 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1780 // TODO: where do set this instead?
1781 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1782 sAchievementMgr.SetRealmCompleted(achievement);
1784 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1786 // reward items and titles if any
1787 AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement, GetPlayer()->getGender());
1789 // no rewards
1790 if(!reward)
1791 return;
1793 // titles
1794 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1796 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1797 GetPlayer()->SetTitle(titleEntry);
1800 // mail
1801 if(reward->sender)
1803 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1805 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1807 // subject and text
1808 std::string subject = reward->subject;
1809 std::string text = reward->text;
1810 if ( loc_idx >= 0 )
1812 if(AchievementRewardLocale const* loc = sAchievementMgr.GetAchievementRewardLocale(achievement, GetPlayer()->getGender()))
1814 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1815 subject = loc->subject[loc_idx];
1816 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1817 text = loc->text[loc_idx];
1821 MailDraft draft(subject, text);
1823 if(item)
1825 // save new item before send
1826 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1828 // item
1829 draft.AddItem(item);
1832 draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender));
1836 void AchievementMgr::SendAllAchievementData()
1838 // since we don't know the exact size of the packed GUIDs this is just an approximation
1839 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1840 BuildAllDataPacket(&data);
1841 GetPlayer()->GetSession()->SendPacket(&data);
1844 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1846 // since we don't know the exact size of the packed GUIDs this is just an approximation
1847 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1848 data << GetPlayer()->GetPackGUID();
1849 BuildAllDataPacket(&data);
1850 player->GetSession()->SendPacket(&data);
1854 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1856 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1858 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1860 *data << uint32(iter->first);
1861 *data << uint32(secsToTimeBitFields(iter->second.date));
1863 *data << int32(-1);
1865 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1867 *data << uint32(iter->first);
1868 data->appendPackGUID(iter->second.counter);
1869 *data << GetPlayer()->GetPackGUID();
1870 *data << uint32(0);
1871 *data << uint32(secsToTimeBitFields(iter->second.date));
1872 *data << uint32(0);
1873 *data << uint32(0);
1876 *data << int32(-1);
1879 //==========================================================
1880 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1882 return m_AchievementCriteriasByType[type];
1885 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1887 if(sAchievementCriteriaStore.GetNumRows()==0)
1889 barGoLink bar(1);
1890 bar.step();
1892 sLog.outString();
1893 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1894 return;
1897 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1898 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1900 bar.step();
1902 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1903 if(!criteria)
1904 continue;
1906 ASSERT(criteria->requiredType < ACHIEVEMENT_CRITERIA_TYPE_TOTAL && "Not updated ACHIEVEMENT_CRITERIA_TYPE_TOTAL?");
1908 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1909 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1912 sLog.outString();
1913 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1916 void AchievementGlobalMgr::LoadAchievementReferenceList()
1918 if(sAchievementStore.GetNumRows()==0)
1920 barGoLink bar(1);
1921 bar.step();
1923 sLog.outString();
1924 sLog.outErrorDb(">> Loaded 0 achievement references.");
1925 return;
1928 uint32 count = 0;
1929 barGoLink bar( sAchievementStore.GetNumRows() );
1930 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1932 bar.step();
1934 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1935 if(!achievement || !achievement->refAchievement)
1936 continue;
1938 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1939 ++count;
1942 sLog.outString();
1943 sLog.outString(">> Loaded %u achievement references.",count);
1946 void AchievementGlobalMgr::LoadAchievementCriteriaRequirements()
1948 m_criteriaRequirementMap.clear(); // need for reload case
1950 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_requirement");
1952 if(!result)
1954 barGoLink bar(1);
1955 bar.step();
1957 sLog.outString();
1958 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_requirement` is empty.");
1959 return;
1962 uint32 count = 0;
1963 uint32 disabled_count = 0;
1964 barGoLink bar((int)result->GetRowCount());
1967 bar.step();
1968 Field *fields = result->Fetch();
1969 uint32 criteria_id = fields[0].GetUInt32();
1971 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1973 if (!criteria)
1975 sLog.outErrorDb( "Table `achievement_criteria_requirement`.`criteria_id` %u does not exist, ignoring.", criteria_id);
1976 continue;
1979 AchievementCriteriaRequirement data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1981 if (!data.IsValid(criteria))
1983 continue;
1986 // this will allocate empty data set storage
1987 AchievementCriteriaRequirementSet& dataSet = m_criteriaRequirementMap[criteria_id];
1988 dataSet.SetCriteriaId(criteria_id);
1990 // counting disable criteria requirements
1991 if (data.requirementType == ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED)
1992 ++disabled_count;
1994 // add real data only for not NONE requirements
1995 if (data.requirementType != ACHIEVEMENT_CRITERIA_REQUIRE_NONE)
1996 dataSet.Add(data);
1998 // counting requirements
1999 ++count;
2000 } while(result->NextRow());
2002 delete result;
2004 // post loading checks
2005 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
2007 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
2008 if(!criteria)
2009 continue;
2011 switch(criteria->requiredType)
2013 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
2014 if(!criteria->win_bg.additionalRequirement1_type && !criteria->win_bg.additionalRequirement2_type)
2015 continue;
2016 break;
2017 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
2018 break; // any cases
2019 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
2021 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
2022 if(!achievement)
2023 continue;
2025 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
2026 switch(achievement->ID)
2028 case 31:
2029 case 1275:
2030 case 1276:
2031 case 1277:
2032 case 1282:
2033 case 1789:
2034 break;
2035 default:
2036 continue;
2039 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
2040 break; // any cases
2041 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
2042 break;
2043 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
2044 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
2045 continue;
2046 break;
2047 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: // any cases
2048 break;
2049 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
2050 if(criteria->do_emote.count==0)
2051 continue;
2052 break;
2053 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:// any cases
2054 break;
2055 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
2056 if(criteria->win_duel.duelCount==0)
2057 continue;
2058 break;
2059 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
2060 break;
2061 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
2062 if(criteria->loot_type.lootTypeCount!=1)
2063 continue;
2064 break;
2065 default: // type not use DB data, ignore
2066 continue;
2069 if (!GetCriteriaRequirementSet(criteria))
2070 sLog.outErrorDb("Table `achievement_criteria_requirement` is missing expected data for `criteria_id` %u (type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement);
2073 sLog.outString();
2074 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
2077 void AchievementGlobalMgr::LoadCompletedAchievements()
2079 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
2081 if(!result)
2083 barGoLink bar(1);
2084 bar.step();
2086 sLog.outString();
2087 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
2088 return;
2091 barGoLink bar((int)result->GetRowCount());
2094 bar.step();
2095 Field *fields = result->Fetch();
2097 uint32 achievement_id = fields[0].GetUInt32();
2098 if(!sAchievementStore.LookupEntry(achievement_id))
2100 // we will remove not existed achievement for all characters
2101 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
2102 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
2103 continue;
2106 m_allCompletedAchievements.insert(achievement_id);
2107 } while(result->NextRow());
2109 delete result;
2111 sLog.outString();
2112 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
2115 void AchievementGlobalMgr::LoadRewards()
2117 m_achievementRewards.clear(); // need for reload case
2119 // 0 1 2 3 4 5 6 7
2120 QueryResult *result = WorldDatabase.Query("SELECT entry, gender, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2122 if(!result)
2124 barGoLink bar(1);
2126 bar.step();
2128 sLog.outString();
2129 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2130 return;
2133 uint32 count = 0;
2134 barGoLink bar((int)result->GetRowCount());
2138 bar.step();
2140 Field *fields = result->Fetch();
2141 uint32 entry = fields[0].GetUInt32();
2142 if (!sAchievementStore.LookupEntry(entry))
2144 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
2145 continue;
2148 AchievementReward reward;
2149 reward.gender = Gender(fields[1].GetUInt8());
2150 reward.titleId[0] = fields[2].GetUInt32();
2151 reward.titleId[1] = fields[3].GetUInt32();
2152 reward.itemId = fields[4].GetUInt32();
2153 reward.sender = fields[5].GetUInt32();
2154 reward.subject = fields[6].GetCppString();
2155 reward.text = fields[7].GetCppString();
2157 if (reward.gender >= MAX_GENDER)
2158 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has wrong gender %u.", entry, reward.gender);
2160 // GENDER_NONE must be single (so or already in and none must be attempt added new data or just adding and none in)
2161 // other duplicate cases prevented by DB primary key
2162 bool dup = false;
2163 AchievementRewards::const_iterator iter_low = m_achievementRewards.lower_bound(entry);
2164 AchievementRewards::const_iterator iter_up = m_achievementRewards.upper_bound(entry);
2165 for (AchievementRewards::const_iterator iter = iter_low; iter != iter_up; ++iter)
2167 if (iter->second.gender == GENDER_NONE || reward.gender == GENDER_NONE)
2169 dup = true;
2170 sLog.outErrorDb( "Table `achievement_reward` must have single GENDER_NONE (%u) case (Entry: %u), ignore duplicate case", GENDER_NONE, entry);
2171 break;
2174 if (dup)
2175 continue;
2177 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
2178 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]);
2180 // must be title or mail at least
2181 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
2183 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
2184 continue;
2187 if (reward.titleId[0])
2189 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
2190 if (!titleEntry)
2192 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
2193 reward.titleId[0] = 0;
2197 if (reward.titleId[1])
2199 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
2200 if (!titleEntry)
2202 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
2203 reward.titleId[1] = 0;
2207 //check mail data before item for report including wrong item case
2208 if (reward.sender)
2210 if (!ObjectMgr::GetCreatureTemplate(reward.sender))
2212 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
2213 reward.sender = 0;
2216 else
2218 if (reward.itemId)
2219 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
2221 if (!reward.subject.empty())
2222 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
2224 if (!reward.text.empty())
2225 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
2228 if (reward.itemId)
2230 if (!ObjectMgr::GetItemPrototype(reward.itemId))
2232 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
2233 reward.itemId = 0;
2237 m_achievementRewards.insert(AchievementRewards::value_type(entry,reward));
2238 ++count;
2240 } while (result->NextRow());
2242 delete result;
2244 sLog.outString();
2245 sLog.outString( ">> Loaded %u achievement rewards", count );
2248 void AchievementGlobalMgr::LoadRewardLocales()
2250 m_achievementRewardLocales.clear(); // need for reload case
2252 QueryResult *result = WorldDatabase.Query("SELECT entry,gender,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");
2254 if(!result)
2256 barGoLink bar(1);
2258 bar.step();
2260 sLog.outString();
2261 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2262 return;
2265 barGoLink bar((int)result->GetRowCount());
2269 Field *fields = result->Fetch();
2270 bar.step();
2272 uint32 entry = fields[0].GetUInt32();
2274 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2276 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2277 continue;
2280 AchievementRewardLocale data;
2282 data.gender = Gender(fields[1].GetUInt8());
2284 if (data.gender >= MAX_GENDER)
2285 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has wrong gender %u.", entry, data.gender);
2287 // GENDER_NONE must be single (so or already in and none must be attempt added new data or just adding and none in)
2288 // other duplicate cases prevented by DB primary key
2289 bool dup = false;
2290 AchievementRewardLocales::const_iterator iter_low = m_achievementRewardLocales.lower_bound(entry);
2291 AchievementRewardLocales::const_iterator iter_up = m_achievementRewardLocales.upper_bound(entry);
2292 for (AchievementRewardLocales::const_iterator iter = iter_low; iter != iter_up; ++iter)
2294 if (iter->second.gender == GENDER_NONE || data.gender == GENDER_NONE)
2296 dup = true;
2297 sLog.outErrorDb( "Table `locales_achievement_reward` must have single GENDER_NONE (%u) case (Entry: %u), ignore duplicate case", GENDER_NONE, entry);
2298 break;
2301 if (dup)
2302 continue;
2304 for(int i = 1; i < MAX_LOCALE; ++i)
2306 std::string str = fields[2+2*(i-1)].GetCppString();
2307 if(!str.empty())
2309 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2310 if(idx >= 0)
2312 if(data.subject.size() <= size_t(idx))
2313 data.subject.resize(idx+1);
2315 data.subject[idx] = str;
2318 str = fields[2+2*(i-1)+1].GetCppString();
2319 if(!str.empty())
2321 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2322 if(idx >= 0)
2324 if(data.text.size() <= size_t(idx))
2325 data.text.resize(idx+1);
2327 data.text[idx] = str;
2332 m_achievementRewardLocales.insert(AchievementRewardLocales::value_type(entry,data));
2334 } while (result->NextRow());
2336 delete result;
2338 sLog.outString();
2339 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );