[9529] Make Player::IsValidPos const
[getmangos.git] / src / game / AchievementMgr.cpp
blob0c27304af3798735c3c61000a98bfd1be97c121c
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 "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_EQUIP_EPIC_ITEM:
93 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
94 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
95 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
96 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
97 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
98 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
99 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
100 break;
101 default:
102 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
103 return false;
106 switch(requirementType)
108 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
109 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
110 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
111 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
112 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
113 return true;
114 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
115 if (!creature.id || !ObjectMgr::GetCreatureTemplate(creature.id))
117 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.",
118 criteria->ID, criteria->requiredType,requirementType,creature.id);
119 return false;
121 return true;
122 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
123 if (!classRace.class_id && !classRace.race_id)
125 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.",
126 criteria->ID, criteria->requiredType,requirementType);
127 return false;
129 if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
131 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.",
132 criteria->ID, criteria->requiredType,requirementType,classRace.class_id);
133 return false;
135 if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
137 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.",
138 criteria->ID, criteria->requiredType,requirementType,classRace.race_id);
139 return false;
141 return true;
142 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
143 if (health.percent < 1 || health.percent > 100)
145 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.",
146 criteria->ID, criteria->requiredType,requirementType,health.percent);
147 return false;
149 return true;
150 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
151 if (player_dead.own_team_flag > 1)
153 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).",
154 criteria->ID, criteria->requiredType,requirementType,player_dead.own_team_flag);
155 return false;
157 return true;
158 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
159 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
161 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
162 if (!spellEntry)
164 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell id in value1 (%u), ignore.",
165 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.spell_id);
166 return false;
168 if (aura.effect_idx >= MAX_EFFECT_INDEX)
170 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell effect index in value2 (%u), ignore.",
171 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.effect_idx);
172 return false;
174 if (!spellEntry->EffectApplyAuraName[aura.effect_idx])
176 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
177 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);
178 return false;
180 return true;
182 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
183 if (!GetAreaEntryByAreaID(area.id))
185 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.",
186 criteria->ID, criteria->requiredType,requirementType,area.id);
187 return false;
189 return true;
190 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
191 if (level.minlevel > STRONG_MAX_LEVEL)
193 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.",
194 criteria->ID, criteria->requiredType,requirementType,level.minlevel);
195 return false;
197 return true;
198 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
199 if (gender.gender > GENDER_NONE)
201 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.",
202 criteria->ID, criteria->requiredType,requirementType,gender.gender);
203 return false;
205 return true;
206 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
207 if (difficulty.difficulty >= MAX_DIFFICULTY)
209 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.",
210 criteria->ID, criteria->requiredType,requirementType,difficulty.difficulty);
211 return false;
213 return true;
214 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
215 if (map_players.maxcount <= 0)
217 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.",
218 criteria->ID, criteria->requiredType,requirementType,map_players.maxcount);
219 return false;
221 return true;
222 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
223 if (team.team != ALLIANCE && team.team != HORDE)
225 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.",
226 criteria->ID, criteria->requiredType,requirementType,team.team);
227 return false;
229 return true;
230 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
231 if(drunk.state >= MAX_DRUNKEN)
233 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.",
234 criteria->ID, criteria->requiredType,requirementType,drunk.state);
235 return false;
237 return true;
238 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
239 if (!sHolidaysStore.LookupEntry(holiday.id))
241 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.",
242 criteria->ID, criteria->requiredType,requirementType,holiday.id);
243 return false;
245 return true;
246 case ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM_LVL:
247 if(equipped_item.item_quality >= MAX_ITEM_QUALITY)
249 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.",
250 criteria->ID, criteria->requiredType,requirementType,equipped_item.item_quality);
251 return false;
253 return true;
254 default:
255 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,requirementType);
256 return false;
258 return false;
261 bool AchievementCriteriaRequirement::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
263 switch(requirementType)
265 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
266 return true;
267 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
268 if (!target || target->GetTypeId()!=TYPEID_UNIT)
269 return false;
270 return target->GetEntry() == creature.id;
271 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
272 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
273 return false;
274 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
275 return false;
276 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
277 return false;
278 return true;
279 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
280 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
281 return false;
282 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
283 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
284 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
285 return false;
286 // flag set == must be same team, not set == different team
287 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
288 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
289 return source->HasAura(aura.spell_id,SpellEffectIndex(aura.effect_idx));
290 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
292 uint32 zone_id,area_id;
293 source->GetZoneAndAreaId(zone_id,area_id);
294 return area.id==zone_id || area.id==area_id;
296 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
297 return target && target->HasAura(aura.spell_id,SpellEffectIndex(aura.effect_idx));
298 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
299 return miscvalue1 >= value.minvalue;
300 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
301 if (!target)
302 return false;
303 return target->getLevel() >= level.minlevel;
304 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
305 if (!target)
306 return false;
307 return target->getGender() == gender.gender;
308 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
309 return false; // always fail
310 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
311 return source->GetMap()->GetSpawnMode()==difficulty.difficulty;
312 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
313 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
314 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
315 if (!target || target->GetTypeId() != TYPEID_PLAYER)
316 return false;
317 return ((Player*)target)->GetTeam() == team.team;
318 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
319 return (uint32)Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state;
320 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
321 return IsHolidayActive(HolidayIds(holiday.id));
322 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
324 BattleGround* bg = source->GetBattleGround();
325 if(!bg)
326 return false;
327 return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score);
329 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
331 if (!source->IsInWorld())
332 return false;
333 Map* map = source->GetMap();
334 if (!map->Instanceable())
336 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-instance map %u",
337 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
338 return false;
340 InstanceData* data = ((InstanceMap*)map)->GetInstanceData();
341 if (!data)
343 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map not have instance script",
344 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
345 return false;
347 return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1);
349 case ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM_LVL:
351 Item* item = source->GetItemByPos(INVENTORY_SLOT_BAG_0,miscvalue1);
352 if (!item)
353 return false;
354 ItemPrototype const* proto = item->GetProto();
355 return proto->ItemLevel >= equipped_item.item_level && proto->Quality >= equipped_item.item_quality;
358 return false;
361 bool AchievementCriteriaRequirementSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
363 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
364 if(!itr->Meets(criteria_id, source, target, miscvalue))
365 return false;
367 return true;
370 AchievementMgr::AchievementMgr(Player *player)
372 m_player = player;
375 AchievementMgr::~AchievementMgr()
379 void AchievementMgr::Reset()
381 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
383 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
384 data << uint32(iter->first);
385 m_player->SendDirectMessage(&data);
388 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
390 WorldPacket data(SMSG_CRITERIA_DELETED,4);
391 data << uint32(iter->first);
392 m_player->SendDirectMessage(&data);
395 m_completedAchievements.clear();
396 m_criteriaProgress.clear();
397 DeleteFromDB(m_player->GetGUIDLow());
399 // re-fill data
400 CheckAllAchievementCriteria();
403 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
405 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
406 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
408 if (!sWorld.getConfig(CONFIG_BOOL_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
409 return;
411 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
412 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
414 AchievementCriteriaEntry const *achievementCriteria = (*i);
416 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
417 if (!achievement)
418 continue;
420 // don't update already completed criteria
421 if (IsCompletedCriteria(achievementCriteria,achievement))
422 continue;
424 switch (type)
426 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
427 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
428 if (achievementCriteria->healing_done.flag == miscvalue1 &&
429 achievementCriteria->healing_done.mapid == miscvalue2)
430 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
431 break;
432 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
433 // reset only the criteria having the miscvalue1 condition
434 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
435 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
436 break;
437 default: // reset all cases
438 break;
443 void AchievementMgr::DeleteFromDB(uint32 lowguid)
445 CharacterDatabase.BeginTransaction ();
446 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
447 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
448 CharacterDatabase.CommitTransaction ();
451 void AchievementMgr::SaveToDB()
453 if(!m_completedAchievements.empty())
455 bool need_execute = false;
456 std::ostringstream ssdel;
457 std::ostringstream ssins;
458 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
460 if(!iter->second.changed)
461 continue;
463 /// first new/changed record prefix
464 if(!need_execute)
466 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
467 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
468 need_execute = true;
470 /// next new/changed record prefix
471 else
473 ssdel << ", ";
474 ssins << ", ";
477 // new/changed record data
478 ssdel << iter->first;
479 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
481 /// mark as saved in db
482 iter->second.changed = false;
485 if(need_execute)
486 ssdel << ")";
488 if(need_execute)
490 CharacterDatabase.Execute( ssdel.str().c_str() );
491 CharacterDatabase.Execute( ssins.str().c_str() );
495 if(!m_criteriaProgress.empty())
497 /// prepare deleting and insert
498 bool need_execute_del = false;
499 bool need_execute_ins = false;
500 std::ostringstream ssdel;
501 std::ostringstream ssins;
502 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
504 if(!iter->second.changed)
505 continue;
507 // deleted data (including 0 progress state)
509 /// first new/changed record prefix (for any counter value)
510 if(!need_execute_del)
512 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
513 need_execute_del = true;
515 /// next new/changed record prefix
516 else
517 ssdel << ", ";
519 // new/changed record data
520 ssdel << iter->first;
523 // store data only for real progress
524 if(iter->second.counter != 0)
526 /// first new/changed record prefix
527 if(!need_execute_ins)
529 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
530 need_execute_ins = true;
532 /// next new/changed record prefix
533 else
534 ssins << ", ";
536 // new/changed record data
537 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
540 /// mark as updated in db
541 iter->second.changed = false;
544 if(need_execute_del) // DELETE ... IN (.... _)_
545 ssdel << ")";
547 if(need_execute_del || need_execute_ins)
549 if(need_execute_del)
550 CharacterDatabase.Execute( ssdel.str().c_str() );
551 if(need_execute_ins)
552 CharacterDatabase.Execute( ssins.str().c_str() );
557 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
559 if(achievementResult)
563 Field *fields = achievementResult->Fetch();
565 uint32 achievement_id = fields[0].GetUInt32();
567 // don't must happen: cleanup at server startup in sAchievementMgr.LoadCompletedAchievements()
568 if(!sAchievementStore.LookupEntry(achievement_id))
569 continue;
571 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
572 ca.date = time_t(fields[1].GetUInt64());
573 ca.changed = false;
574 } while(achievementResult->NextRow());
575 delete achievementResult;
578 if(criteriaResult)
582 Field *fields = criteriaResult->Fetch();
584 uint32 id = fields[0].GetUInt32();
585 uint32 counter = fields[1].GetUInt32();
586 time_t date = time_t(fields[2].GetUInt64());
588 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
589 if (!criteria)
591 // we will remove not existed criteria for all characters
592 sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id);
593 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
594 continue;
597 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
598 continue;
600 CriteriaProgress& progress = m_criteriaProgress[id];
601 progress.counter = counter;
602 progress.date = date;
603 progress.changed = false;
604 } while(criteriaResult->NextRow());
605 delete criteriaResult;
610 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
612 if(GetPlayer()->GetSession()->PlayerLoading())
613 return;
615 #ifdef MANGOS_DEBUG
616 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
617 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
618 #endif
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 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
642 Cell cell(p);
643 cell.data.Part.reserved = ALL_DISTRICT;
644 cell.SetNoCreate();
646 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
647 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
648 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY),say_do);
649 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
650 cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY));
653 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
654 data.append(GetPlayer()->GetPackGUID());
655 data << uint32(achievement->ID);
656 data << uint32(secsToTimeBitFields(time(NULL)));
657 data << uint32(0);
658 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY), true);
661 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
663 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
664 data << uint32(id);
666 // the counter is packed like a packed Guid
667 data.appendPackGUID(progress->counter);
669 data.append(GetPlayer()->GetPackGUID());
670 data << uint32(0);
671 data << uint32(secsToTimeBitFields(progress->date));
672 data << uint32(0); // timer 1
673 data << uint32(0); // timer 2
674 GetPlayer()->SendDirectMessage(&data);
678 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
680 void AchievementMgr::CheckAllAchievementCriteria()
682 // suppress sending packets
683 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
684 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
687 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
688 static const uint32 achievIdForDangeon[][4] =
690 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
691 { 321, true, true, true },
692 { 916, false, true, false },
693 { 917, false, true, false },
694 { 918, true, false, false },
695 { 2219, false, false, true },
696 { 0, false, false, false }
700 * this function will be called whenever the user might have done a criteria relevant action
702 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
704 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
705 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
707 if (!sWorld.getConfig(CONFIG_BOOL_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
708 return;
710 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
711 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
713 AchievementCriteriaEntry const *achievementCriteria = (*i);
715 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
716 continue;
718 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
719 if (!achievement)
720 continue;
722 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
723 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
724 continue;
726 // don't update already completed criteria
727 if (IsCompletedCriteria(achievementCriteria,achievement))
728 continue;
730 switch (type)
732 // std. case: increment at 1
733 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
734 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
735 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
736 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
737 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */
738 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
739 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
740 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
741 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
742 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
743 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
744 if(!miscvalue1)
745 continue;
746 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
747 break;
748 // std case: increment at miscvalue1
749 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
750 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
751 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
752 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
753 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
754 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
755 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
756 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */
757 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
758 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
759 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
760 if(!miscvalue1)
761 continue;
762 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
763 break;
764 // std case: high value at miscvalue1
765 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
766 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
767 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
768 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
769 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
770 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
771 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
772 if(!miscvalue1)
773 continue;
774 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
775 break;
777 // specialized cases
779 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
781 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
782 if (!miscvalue1)
783 continue;
784 if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
785 continue;
787 if (achievementCriteria->win_bg.additionalRequirement1_type)
789 // those requirements couldn't be found in the dbc
790 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
791 if (!data || !data->Meets(GetPlayer(),unit))
792 continue;
794 // some hardcoded requirements
795 else
797 BattleGround* bg = GetPlayer()->GetBattleGround();
798 if (!bg)
799 continue;
801 switch(achievementCriteria->referredAchievement)
803 case 161: // AB, Overcome a 500 resource disadvantage
805 if (bg->GetTypeID() != BATTLEGROUND_AB)
806 continue;
807 if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
808 continue;
809 break;
811 case 156: // AB, win while controlling all 5 flags (all nodes)
812 case 784: // EY, win while holding 4 bases (all nodes)
814 if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam()))
815 continue;
816 break;
818 case 1762: // SA, win without losing any siege vehicles
819 case 2192: // SA, win without losing any siege vehicles
820 continue; // not implemented
824 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
825 break;
827 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
829 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
830 if(!miscvalue1)
831 continue;
832 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
833 continue;
835 // those requirements couldn't be found in the dbc
836 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
837 if(!data || !data->Meets(GetPlayer(),unit))
838 continue;
840 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
841 break;
843 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
844 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
845 break;
846 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
847 // update at loading or specific skill update
848 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
849 continue;
850 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
851 SetCriteriaProgress(achievementCriteria, skillvalue);
852 break;
853 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
854 // update at loading or specific skill update
855 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
856 continue;
857 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
858 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
859 break;
860 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
861 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
862 SetCriteriaProgress(achievementCriteria, 1);
863 break;
864 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
866 uint32 counter =0;
867 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
868 if(itr->second.m_rewarded)
869 counter++;
870 SetCriteriaProgress(achievementCriteria, counter);
871 break;
873 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
875 // speedup for non-login case
876 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
877 continue;
879 uint32 counter =0;
880 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
882 Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first);
883 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
884 counter++;
886 SetCriteriaProgress(achievementCriteria, counter);
887 break;
889 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
890 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
891 if(!miscvalue1)
892 continue;
893 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
894 continue;
895 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
896 break;
897 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
898 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
899 if(!miscvalue1)
900 continue;
901 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
902 continue;
903 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
904 break;
905 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
907 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
908 if(!miscvalue1)
909 continue;
910 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
911 bool notfit = false;
912 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
914 if(achievIdByArenaSlot[j] == achievement->ID)
916 BattleGround* bg = GetPlayer()->GetBattleGround();
917 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
918 notfit = true;
920 break;
923 if(notfit)
924 continue;
926 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
927 break;
929 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
931 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
932 if(!miscvalue1)
933 continue;
935 Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr.FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
936 if(!map || !map->IsDungeon())
937 continue;
939 // search case
940 bool found = false;
941 for(int j = 0; achievIdForDangeon[j][0]; ++j)
943 if(achievIdForDangeon[j][0] == achievement->ID)
945 if(map->IsRaid())
947 // if raid accepted (ignore difficulty)
948 if(!achievIdForDangeon[j][2])
949 break; // for
951 else if(GetPlayer()->GetDungeonDifficulty()==DUNGEON_DIFFICULTY_NORMAL)
953 // dungeon in normal mode accepted
954 if(!achievIdForDangeon[j][1])
955 break; // for
957 else
959 // dungeon in heroic mode accepted
960 if(!achievIdForDangeon[j][3])
961 break; // for
964 found = true;
965 break; // for
968 if(!found)
969 continue;
971 //FIXME: work only for instances where max==min for players
972 if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
973 continue;
974 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
975 break;
978 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
979 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
980 if(!miscvalue1)
981 continue;
982 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
983 continue;
984 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
985 break;
986 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
987 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
988 if(!miscvalue1)
989 continue;
991 // if team check required: must kill by opposition faction
992 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
993 continue;
995 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
996 break;
997 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
999 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1000 if(!miscvalue1)
1001 continue;
1003 // those requirements couldn't be found in the dbc
1004 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1005 if(!data || !data->Meets(GetPlayer(),unit))
1006 continue;
1008 // miscvalue1 is the ingame fallheight*100 as stored in dbc
1009 SetCriteriaProgress(achievementCriteria, miscvalue1);
1010 break;
1012 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1013 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1014 if(!miscvalue1)
1015 continue;
1016 if(miscvalue2 != achievementCriteria->death_from.type)
1017 continue;
1018 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1019 break;
1020 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1022 // if miscvalues != 0, it contains the questID.
1023 if (miscvalue1)
1025 if (miscvalue1 != achievementCriteria->complete_quest.questID)
1026 continue;
1028 else
1030 // login case.
1031 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
1032 continue;
1035 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1036 switch(achievement->ID)
1038 case 31:
1039 case 1275:
1040 case 1276:
1041 case 1277:
1042 case 1282:
1043 case 1789:
1045 // those requirements couldn't be found in the dbc
1046 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1047 if(!data || !data->Meets(GetPlayer(),unit))
1048 continue;
1049 break;
1051 default:
1052 break;
1056 SetCriteriaProgress(achievementCriteria, 1);
1057 break;
1059 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1060 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1062 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
1063 continue;
1065 // those requirements couldn't be found in the dbc
1066 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1067 if(!data)
1068 continue;
1070 if(!data->Meets(GetPlayer(),unit))
1071 continue;
1073 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1074 break;
1076 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1077 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1079 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1080 continue;
1082 // those requirements couldn't be found in the dbc
1083 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1084 if(!data)
1085 continue;
1087 if(!data->Meets(GetPlayer(),unit))
1088 continue;
1090 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1091 break;
1093 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1094 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
1095 continue;
1097 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
1098 SetCriteriaProgress(achievementCriteria, 1);
1099 break;
1100 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1102 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
1103 // miscvalue2=count of item loot
1104 if (!miscvalue1 || !miscvalue2)
1105 continue;
1106 if (miscvalue1 != achievementCriteria->loot_type.lootType)
1107 continue;
1109 // zone specific
1110 if(achievementCriteria->loot_type.lootTypeCount==1)
1112 // those requirements couldn't be found in the dbc
1113 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1114 if(!data || !data->Meets(GetPlayer(),unit))
1115 continue;
1118 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1119 break;
1121 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1122 // speedup for non-login case
1123 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
1124 continue;
1125 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
1126 break;
1127 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1128 // miscvalue1 contains the personal rating
1129 if (!miscvalue1) // no update at login
1130 continue;
1132 // additional requirements
1133 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1135 // those requirements couldn't be found in the dbc
1136 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1137 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
1139 // reset the progress as we have a win without the requirement.
1140 SetCriteriaProgress(achievementCriteria, 0);
1141 continue;
1145 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1146 break;
1147 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1148 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1149 if(!miscvalue1)
1150 continue;
1151 if(achievementCriteria->use_item.itemID != miscvalue1)
1152 continue;
1153 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1154 break;
1155 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1156 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1157 if(!miscvalue1)
1158 continue;
1159 if(miscvalue1 != achievementCriteria->own_item.itemID)
1160 continue;
1161 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1162 break;
1163 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1165 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
1166 if(!worldOverlayEntry)
1167 break;
1169 bool matchFound = false;
1170 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
1172 uint32 area_id = worldOverlayEntry->areatableID[j];
1173 if(!area_id) // array have 0 only in empty tail
1174 break;
1176 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
1177 if(exploreFlag < 0)
1178 continue;
1180 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
1181 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1183 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1185 matchFound = true;
1186 break;
1190 if(matchFound)
1191 SetCriteriaProgress(achievementCriteria, 1);
1192 break;
1194 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1195 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1196 break;
1197 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1199 // skip faction check only at loading
1200 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1201 continue;
1203 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1204 if (reputation > 0)
1205 SetCriteriaProgress(achievementCriteria, reputation);
1206 break;
1208 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1210 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1211 break;
1213 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1215 // skip for login case
1216 if(!miscvalue1)
1217 continue;
1218 SetCriteriaProgress(achievementCriteria, 1);
1219 break;
1221 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1223 // miscvalue1 = equip_slot+1 (for avoid use 0)
1224 if(!miscvalue1)
1225 continue;
1226 uint32 item_slot = miscvalue1-1;
1227 if(item_slot != achievementCriteria->equip_epic_item.itemSlot)
1228 continue;
1229 // those requirements couldn't be found in the dbc
1230 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1231 if(!data || !data->Meets(GetPlayer(),unit,item_slot))
1232 continue;
1233 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_SET);
1234 break;
1236 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1237 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1239 // miscvalue1 = itemid
1240 // miscvalue2 = diced value
1241 if(!miscvalue1)
1242 continue;
1243 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1244 continue;
1245 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( miscvalue1 );
1247 uint32 requiredItemLevel = 0;
1248 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1249 requiredItemLevel = 185;
1251 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1252 continue;
1253 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1254 break;
1256 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1258 // miscvalue1 = emote
1259 if(!miscvalue1)
1260 continue;
1261 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1262 continue;
1263 if(achievementCriteria->do_emote.count)
1265 // those requirements couldn't be found in the dbc
1266 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1267 if(!data || !data->Meets(GetPlayer(),unit))
1268 continue;
1271 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1272 break;
1274 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1275 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1277 if (!miscvalue1)
1278 continue;
1280 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1282 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1283 continue;
1285 // map specific case (BG in fact) expected player targeted damage/heal
1286 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1287 continue;
1290 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1291 break;
1293 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1294 // miscvalue1 = item_id
1295 if (!miscvalue1)
1296 continue;
1297 if (miscvalue1 != achievementCriteria->equip_item.itemID)
1298 continue;
1300 SetCriteriaProgress(achievementCriteria, 1);
1301 break;
1302 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1303 // miscvalue1 = go entry
1304 if (!miscvalue1)
1305 continue;
1306 if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1307 continue;
1309 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1310 break;
1311 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1313 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1314 if (!miscvalue1)
1315 continue;
1317 // those requirements couldn't be found in the dbc
1318 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1319 if (!data)
1320 continue;
1322 if (!data->Meets(GetPlayer(),unit))
1323 continue;
1325 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1326 break;
1328 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1329 if (!miscvalue1)
1330 continue;
1331 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1332 continue;
1334 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1335 break;
1336 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1338 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1339 continue;
1341 uint32 spellCount = 0;
1342 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1343 spellIter != GetPlayer()->GetSpellMap().end();
1344 ++spellIter)
1346 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1347 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1349 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1350 spellCount++;
1353 SetCriteriaProgress(achievementCriteria, spellCount);
1354 break;
1356 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1357 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1358 if (!miscvalue1)
1359 continue;
1361 if (achievementCriteria->win_duel.duelCount)
1363 // those requirements couldn't be found in the dbc
1364 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1365 if (!data)
1366 continue;
1368 if (!data->Meets(GetPlayer(),unit))
1369 continue;
1372 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1373 break;
1374 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1375 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1376 break;
1377 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1378 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1379 break;
1380 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1381 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1382 break;
1383 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1384 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1386 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1387 if (!miscvalue1)
1388 continue;
1389 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1);
1390 if (!proto || proto->Quality < ITEM_QUALITY_EPIC)
1391 continue;
1392 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1393 break;
1395 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1397 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1398 continue;
1400 uint32 spellCount = 0;
1401 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1402 spellIter != GetPlayer()->GetSpellMap().end();
1403 ++spellIter)
1405 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1406 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1407 if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1408 spellCount++;
1410 SetCriteriaProgress(achievementCriteria, spellCount);
1411 break;
1413 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1414 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1415 break;
1416 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1417 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1418 continue;
1420 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1421 break;
1422 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1423 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1424 continue;
1426 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1427 break;
1428 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1429 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1430 break;
1431 // std case: not exist in DBC, not triggered in code as result
1432 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1433 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1434 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1435 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1436 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1437 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1438 break;
1439 // FIXME: not triggered in code as result, need to implement
1440 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1441 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1442 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1443 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1444 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1445 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1446 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1447 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1448 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1449 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1450 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1451 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1452 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1453 case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS:
1454 case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
1455 break; // Not implemented yet :(
1457 if(IsCompletedCriteria(achievementCriteria,achievement))
1458 CompletedCriteriaFor(achievement);
1460 // check again the completeness for SUMM and REQ COUNT achievements,
1461 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1462 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1464 if (IsCompletedAchievement(achievement))
1465 CompletedAchievement(achievement);
1468 if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
1470 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1471 if(IsCompletedAchievement(*itr))
1472 CompletedAchievement(*itr);
1477 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1478 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1480 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1482 // counter can never complete
1483 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1484 return false;
1486 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1488 // someone on this realm has already completed that achievement
1489 if(sAchievementMgr.IsRealmCompleted(achievement))
1490 return false;
1493 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1494 if(itr == m_criteriaProgress.end())
1495 return false;
1497 CriteriaProgress const* progress = &itr->second;
1499 switch(achievementCriteria->requiredType)
1501 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1502 return progress->counter >= achievementCriteria->win_bg.winCount;
1503 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1504 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1505 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1507 // skip wrong class achievements
1508 for(int i = 1; i < MAX_CLASSES; ++i)
1509 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1510 return false;
1512 // skip wrong race achievements
1513 for(int i = 1; i < MAX_RACES; ++i)
1514 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1515 return false;
1517 // appropriate class/race or not class/race specific
1518 return progress->counter >= achievementCriteria->reach_level.level;
1520 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1521 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1522 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1523 return progress->counter >= 1;
1524 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1525 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1526 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1527 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1528 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1529 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1530 return progress->counter >= achievementCriteria->healing_done.count;
1531 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1532 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1533 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1534 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1535 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1536 return progress->counter >= 1;
1537 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1538 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1539 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1540 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1541 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1542 return progress->counter >= achievementCriteria->cast_spell.castCount;
1543 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1544 return progress->counter >= 1;
1545 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1546 return progress->counter >= achievementCriteria->own_item.itemCount;
1547 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1548 return progress->counter >= achievementCriteria->win_rated_arena.count;
1549 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1550 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1551 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1552 return progress->counter >= achievementCriteria->use_item.itemCount;
1553 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1554 return progress->counter >= achievementCriteria->loot_item.itemCount;
1555 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1556 return progress->counter >= 1;
1557 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1558 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1559 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1560 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1561 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1562 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1563 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1564 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1565 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1566 return progress->counter >= achievementCriteria->equip_epic_item.count;
1567 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1568 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1569 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1570 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1571 return progress->counter >= achievementCriteria->hk_class.count;
1572 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1573 return progress->counter >= achievementCriteria->hk_race.count;
1574 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1575 return progress->counter >= achievementCriteria->do_emote.count;
1576 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1577 return progress->counter >= achievementCriteria->equip_item.count;
1578 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1579 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1580 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1581 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1582 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1583 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1584 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1585 return progress->counter >= achievementCriteria->special_pvp_kill.killCount;
1586 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1587 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1588 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1589 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1590 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1591 return progress->counter >= achievementCriteria->win_duel.duelCount;
1592 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1593 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1594 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1595 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1596 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1597 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1599 // handle all statistic-only criteria here
1600 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1601 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1602 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1603 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1604 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1605 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1606 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1607 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1608 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1609 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1610 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1611 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1612 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1613 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1614 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1615 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1616 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1617 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1618 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1619 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1620 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1621 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1622 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1623 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1624 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1625 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1626 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1627 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1628 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1629 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1630 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1631 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1632 return false;
1634 return false;
1637 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1639 // counter can never complete
1640 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1641 return;
1643 // already completed and stored
1644 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1645 return;
1647 if (IsCompletedAchievement(achievement))
1648 CompletedAchievement(achievement);
1651 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1653 // counter can never complete
1654 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1655 return false;
1657 // for achievement with referenced achievement criterias get from referenced and counter from self
1658 uint32 achievementForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1659 uint32 achievementForTestCount = entry->count;
1661 AchievementCriteriaEntryList const* cList = sAchievementMgr.GetAchievementCriteriaByAchievement(achievementForTestId);
1662 if(!cList)
1663 return false;
1664 uint32 count = 0;
1666 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1667 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1668 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1670 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1672 AchievementCriteriaEntry const* criteria = *itr;
1674 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1675 if(itrProgress == m_criteriaProgress.end())
1676 continue;
1678 CriteriaProgress const* progress = &itrProgress->second;
1679 count += progress->counter;
1681 // for counters, field4 contains the main count requirement
1682 if (count >= criteria->raw.count)
1683 return true;
1685 return false;
1688 // Default case - need complete all or
1689 bool completed_all = true;
1690 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1692 AchievementCriteriaEntry const* criteria = *itr;
1694 bool completed = IsCompletedCriteria(criteria,entry);
1696 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1697 if(completed)
1698 ++count;
1699 else
1700 completed_all = false;
1702 // completed as have req. count of completed criterias
1703 if(achievementForTestCount > 0 && achievementForTestCount <= count)
1704 return true;
1707 // all criterias completed requirement
1708 if(completed_all && achievementForTestCount==0)
1709 return true;
1711 return false;
1714 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1716 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1717 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1719 CriteriaProgress *progress = NULL;
1721 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1723 if(iter == m_criteriaProgress.end())
1725 // not create record for 0 counter
1726 if(changeValue == 0)
1727 return;
1729 progress = &m_criteriaProgress[entry->ID];
1730 progress->counter = changeValue;
1731 progress->date = time(NULL);
1733 else
1735 progress = &iter->second;
1737 uint32 newValue = 0;
1738 switch(ptype)
1740 case PROGRESS_SET:
1741 newValue = changeValue;
1742 break;
1743 case PROGRESS_ACCUMULATE:
1745 // avoid overflow
1746 uint32 max_value = std::numeric_limits<uint32>::max();
1747 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1748 break;
1750 case PROGRESS_HIGHEST:
1751 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1752 break;
1755 // not update (not mark as changed) if counter will have same value
1756 if(progress->counter == newValue)
1757 return;
1759 progress->counter = newValue;
1762 progress->changed = true;
1764 if(entry->timeLimit)
1766 time_t now = time(NULL);
1767 if(time_t(progress->date + entry->timeLimit) < now)
1768 progress->counter = 1;
1770 // also it seems illogical, the timeframe will be extended at every criteria update
1771 progress->date = now;
1773 SendCriteriaUpdate(entry->ID,progress);
1776 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1778 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1779 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1780 return;
1782 SendAchievementEarned(achievement);
1783 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1784 ca.date = time(NULL);
1785 ca.changed = true;
1787 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1788 // TODO: where do set this instead?
1789 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1790 sAchievementMgr.SetRealmCompleted(achievement);
1792 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1794 // reward items and titles if any
1795 AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement);
1797 // no rewards
1798 if(!reward)
1799 return;
1801 // titles
1802 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1804 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1805 GetPlayer()->SetTitle(titleEntry);
1808 // mail
1809 if(reward->sender)
1811 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1813 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1815 // subject and text
1816 std::string subject = reward->subject;
1817 std::string text = reward->text;
1818 if ( loc_idx >= 0 )
1820 if(AchievementRewardLocale const* loc = sAchievementMgr.GetAchievementRewardLocale(achievement))
1822 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1823 subject = loc->subject[loc_idx];
1824 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1825 text = loc->text[loc_idx];
1829 uint32 itemTextId = sObjectMgr.CreateItemText( text );
1831 MailDraft draft(subject, itemTextId);
1833 if(item)
1835 // save new item before send
1836 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1838 // item
1839 draft.AddItem(item);
1842 draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender));
1846 void AchievementMgr::SendAllAchievementData()
1848 // since we don't know the exact size of the packed GUIDs this is just an approximation
1849 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1850 BuildAllDataPacket(&data);
1851 GetPlayer()->GetSession()->SendPacket(&data);
1854 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1856 // since we don't know the exact size of the packed GUIDs this is just an approximation
1857 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1858 data.append(GetPlayer()->GetPackGUID());
1859 BuildAllDataPacket(&data);
1860 player->GetSession()->SendPacket(&data);
1864 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1866 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1868 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1870 *data << uint32(iter->first);
1871 *data << uint32(secsToTimeBitFields(iter->second.date));
1873 *data << int32(-1);
1875 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1877 *data << uint32(iter->first);
1878 data->appendPackGUID(iter->second.counter);
1879 data->append(GetPlayer()->GetPackGUID());
1880 *data << uint32(0);
1881 *data << uint32(secsToTimeBitFields(iter->second.date));
1882 *data << uint32(0);
1883 *data << uint32(0);
1886 *data << int32(-1);
1889 //==========================================================
1890 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1892 return m_AchievementCriteriasByType[type];
1895 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1897 if(sAchievementCriteriaStore.GetNumRows()==0)
1899 barGoLink bar(1);
1900 bar.step();
1902 sLog.outString();
1903 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1904 return;
1907 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1908 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1910 bar.step();
1912 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1913 if(!criteria)
1914 continue;
1916 assert(criteria->requiredType < ACHIEVEMENT_CRITERIA_TYPE_TOTAL && "Not updated ACHIEVEMENT_CRITERIA_TYPE_TOTAL?");
1918 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1919 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1922 sLog.outString();
1923 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1926 void AchievementGlobalMgr::LoadAchievementReferenceList()
1928 if(sAchievementStore.GetNumRows()==0)
1930 barGoLink bar(1);
1931 bar.step();
1933 sLog.outString();
1934 sLog.outErrorDb(">> Loaded 0 achievement references.");
1935 return;
1938 uint32 count = 0;
1939 barGoLink bar( sAchievementStore.GetNumRows() );
1940 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1942 bar.step();
1944 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1945 if(!achievement || !achievement->refAchievement)
1946 continue;
1948 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1949 ++count;
1952 sLog.outString();
1953 sLog.outString(">> Loaded %u achievement references.",count);
1956 void AchievementGlobalMgr::LoadAchievementCriteriaRequirements()
1958 m_criteriaRequirementMap.clear(); // need for reload case
1960 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_requirement");
1962 if(!result)
1964 barGoLink bar(1);
1965 bar.step();
1967 sLog.outString();
1968 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_requirement` is empty.");
1969 return;
1972 uint32 count = 0;
1973 uint32 disabled_count = 0;
1974 barGoLink bar((int)result->GetRowCount());
1977 bar.step();
1978 Field *fields = result->Fetch();
1979 uint32 criteria_id = fields[0].GetUInt32();
1981 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1983 if (!criteria)
1985 sLog.outErrorDb( "Table `achievement_criteria_requirement`.`criteria_id` %u does not exist, ignoring.", criteria_id);
1986 continue;
1989 AchievementCriteriaRequirement data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1991 if (!data.IsValid(criteria))
1993 continue;
1996 // this will allocate empty data set storage
1997 AchievementCriteriaRequirementSet& dataSet = m_criteriaRequirementMap[criteria_id];
1998 dataSet.SetCriteriaId(criteria_id);
2000 // counting disable criteria requirements
2001 if (data.requirementType == ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED)
2002 ++disabled_count;
2004 // add real data only for not NONE requirements
2005 if (data.requirementType != ACHIEVEMENT_CRITERIA_REQUIRE_NONE)
2006 dataSet.Add(data);
2008 // counting requirements
2009 ++count;
2010 } while(result->NextRow());
2012 delete result;
2014 // post loading checks
2015 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
2017 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
2018 if(!criteria)
2019 continue;
2021 switch(criteria->requiredType)
2023 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
2024 if(!criteria->win_bg.additionalRequirement1_type)
2025 continue;
2026 break;
2027 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
2028 break; // any cases
2029 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
2031 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
2032 if(!achievement)
2033 continue;
2035 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
2036 switch(achievement->ID)
2038 case 31:
2039 case 1275:
2040 case 1276:
2041 case 1277:
2042 case 1282:
2043 case 1789:
2044 break;
2045 default:
2046 continue;
2049 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
2050 break; // any cases
2051 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
2052 break;
2053 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
2054 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
2055 continue;
2056 break;
2057 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: // any cases
2058 break;
2059 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
2060 if(criteria->do_emote.count==0)
2061 continue;
2062 break;
2063 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:// any cases
2064 break;
2065 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
2066 if(criteria->win_duel.duelCount==0)
2067 continue;
2068 break;
2069 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
2070 break;
2071 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
2072 if(criteria->loot_type.lootTypeCount!=1)
2073 continue;
2074 break;
2075 default: // type not use DB data, ignore
2076 continue;
2079 if (!GetCriteriaRequirementSet(criteria))
2080 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);
2083 sLog.outString();
2084 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
2087 void AchievementGlobalMgr::LoadCompletedAchievements()
2089 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
2091 if(!result)
2093 barGoLink bar(1);
2094 bar.step();
2096 sLog.outString();
2097 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
2098 return;
2101 barGoLink bar((int)result->GetRowCount());
2104 bar.step();
2105 Field *fields = result->Fetch();
2107 uint32 achievement_id = fields[0].GetUInt32();
2108 if(!sAchievementStore.LookupEntry(achievement_id))
2110 // we will remove not existed achievement for all characters
2111 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
2112 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
2113 continue;
2116 m_allCompletedAchievements.insert(achievement_id);
2117 } while(result->NextRow());
2119 delete result;
2121 sLog.outString();
2122 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
2125 void AchievementGlobalMgr::LoadRewards()
2127 m_achievementRewards.clear(); // need for reload case
2129 // 0 1 2 3 4 5 6
2130 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2132 if(!result)
2134 barGoLink bar(1);
2136 bar.step();
2138 sLog.outString();
2139 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2140 return;
2143 uint32 count = 0;
2144 barGoLink bar((int)result->GetRowCount());
2148 bar.step();
2150 Field *fields = result->Fetch();
2151 uint32 entry = fields[0].GetUInt32();
2152 if (!sAchievementStore.LookupEntry(entry))
2154 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
2155 continue;
2158 AchievementReward reward;
2159 reward.titleId[0] = fields[1].GetUInt32();
2160 reward.titleId[1] = fields[2].GetUInt32();
2161 reward.itemId = fields[3].GetUInt32();
2162 reward.sender = fields[4].GetUInt32();
2163 reward.subject = fields[5].GetCppString();
2164 reward.text = fields[6].GetCppString();
2166 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
2167 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]);
2169 // must be title or mail at least
2170 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
2172 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
2173 continue;
2176 if (reward.titleId[0])
2178 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
2179 if (!titleEntry)
2181 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
2182 reward.titleId[0] = 0;
2186 if (reward.titleId[1])
2188 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
2189 if (!titleEntry)
2191 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
2192 reward.titleId[1] = 0;
2196 //check mail data before item for report including wrong item case
2197 if (reward.sender)
2199 if (!ObjectMgr::GetCreatureTemplate(reward.sender))
2201 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
2202 reward.sender = 0;
2205 else
2207 if (reward.itemId)
2208 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
2210 if (!reward.subject.empty())
2211 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
2213 if (!reward.text.empty())
2214 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
2217 if (reward.itemId)
2219 if (!ObjectMgr::GetItemPrototype(reward.itemId))
2221 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
2222 reward.itemId = 0;
2226 m_achievementRewards[entry] = reward;
2227 ++count;
2229 } while (result->NextRow());
2231 delete result;
2233 sLog.outString();
2234 sLog.outString( ">> Loaded %u achievement rewards", count );
2237 void AchievementGlobalMgr::LoadRewardLocales()
2239 m_achievementRewardLocales.clear(); // need for reload case
2241 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");
2243 if(!result)
2245 barGoLink bar(1);
2247 bar.step();
2249 sLog.outString();
2250 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2251 return;
2254 barGoLink bar((int)result->GetRowCount());
2258 Field *fields = result->Fetch();
2259 bar.step();
2261 uint32 entry = fields[0].GetUInt32();
2263 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2265 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2266 continue;
2269 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
2271 for(int i = 1; i < MAX_LOCALE; ++i)
2273 std::string str = fields[1+2*(i-1)].GetCppString();
2274 if(!str.empty())
2276 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2277 if(idx >= 0)
2279 if(data.subject.size() <= size_t(idx))
2280 data.subject.resize(idx+1);
2282 data.subject[idx] = str;
2285 str = fields[1+2*(i-1)+1].GetCppString();
2286 if(!str.empty())
2288 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2289 if(idx >= 0)
2291 if(data.text.size() <= size_t(idx))
2292 data.text.resize(idx+1);
2294 data.text[idx] = str;
2298 } while (result->NextRow());
2300 delete result;
2302 sLog.outString();
2303 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );