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
20 #include "AchievementMgr.h"
22 #include "WorldPacket.h"
24 #include "GameEventMgr.h"
25 #include "ObjectMgr.h"
27 #include "Database/DatabaseEnv.h"
30 #include "ArenaTeam.h"
31 #include "ProgressBar.h"
33 #include "GridNotifiersImpl.h"
36 #include "MapManager.h"
37 #include "BattleGround.h"
38 #include "BattleGroundAB.h"
40 #include "InstanceData.h"
42 #include "Policies/SingletonImp.h"
44 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr
);
48 class AchievementChatBuilder
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());
61 data
<< uint64(i_player
.GetGUID());
62 data
<< uint32(strlen(text
)+1);
65 data
<< uint32(i_achievementId
);
69 Player
const& i_player
;
72 uint32 i_achievementId
;
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
);
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
:
103 sLog
.outErrorDb( "Table `achievement_criteria_requirement` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria
->ID
, criteria
->requiredType
);
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
:
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
);
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
);
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
);
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
);
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
);
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
);
159 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA
:
160 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA
:
162 SpellEntry
const* spellEntry
= sSpellStore
.LookupEntry(aura
.spell_id
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
:
268 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE
:
269 if (!target
|| target
->GetTypeId()!=TYPEID_UNIT
)
271 return target
->GetEntry() == creature
.id
;
272 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE
:
273 if (!target
|| target
->GetTypeId()!=TYPEID_PLAYER
)
275 if(classRace
.class_id
&& classRace
.class_id
!= ((Player
*)target
)->getClass())
277 if(classRace
.race_id
&& classRace
.race_id
!= ((Player
*)target
)->getRace())
280 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH
:
281 if (!target
|| target
->GetTypeId()!=TYPEID_PLAYER
)
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)
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
:
304 return target
->getLevel() >= level
.minlevel
;
305 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER
:
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
)
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();
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())
334 Map
* map
= source
->GetMap();
335 // BattleGroundMap-class is instanceable, but no InstanceMap-class
336 if (map
->IsBattleGroundOrArena())
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());
344 InstanceData
* data
= ((InstanceMap
*)map
)->GetInstanceData();
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());
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
);
358 ItemPrototype
const* proto
= item
->GetProto();
359 return proto
->ItemLevel
>= equipped_item
.item_level
&& proto
->Quality
>= equipped_item
.item_quality
;
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
))
374 AchievementMgr::AchievementMgr(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());
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
)
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
);
423 // don't update already completed criteria
424 if (IsCompletedCriteria(achievementCriteria
,achievement
))
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
);
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
);
440 default: // reset all cases
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
)
466 /// first new/changed record prefix
469 ssdel
<< "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
470 ssins
<< "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
473 /// next new/changed record prefix
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;
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
)
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
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
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 (.... _)_
550 if(need_execute_del
|| need_execute_ins
)
553 CharacterDatabase
.Execute( ssdel
.str().c_str() );
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
))
574 CompletedAchievementData
& ca
= m_completedAchievements
[achievement_id
];
575 ca
.date
= time_t(fields
[1].GetUInt64());
577 } while(achievementResult
->NextRow());
578 delete achievementResult
;
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
);
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
);
600 if (criteria
->timeLimit
&& time_t(date
+ criteria
->timeLimit
) < time(NULL
))
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())
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
)));
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);
660 // the counter is packed like a packed Guid
661 data
.appendPackGUID(progress
->counter
);
663 data
<< GetPlayer()->GetPackGUID();
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
)
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())
711 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
715 if ((achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_HORDE
&& GetPlayer()->GetTeam() != HORDE
) ||
716 (achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_ALLIANCE
&& GetPlayer()->GetTeam() != ALLIANCE
))
719 // don't update already completed criteria
720 if (IsCompletedCriteria(achievementCriteria
,achievement
))
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
739 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
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
755 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
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
767 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_HIGHEST
);
772 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG
:
774 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
777 if (achievementCriteria
->win_bg
.bgMapID
!= GetPlayer()->GetMapId())
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
))
787 // some hardcoded requirements
790 BattleGround
* bg
= GetPlayer()->GetBattleGround();
794 switch(achievementCriteria
->referredAchievement
)
796 case 161: // AB, Overcome a 500 resource disadvantage
798 if (bg
->GetTypeID() != BATTLEGROUND_AB
)
800 if(!((BattleGroundAB
*)bg
)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
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()))
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
);
820 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
822 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
825 if(achievementCriteria
->kill_creature
.creatureID
!= miscvalue1
)
828 // those requirements couldn't be found in the dbc
829 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
830 if(!data
|| !data
->Meets(GetPlayer(),unit
))
833 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
836 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
837 SetCriteriaProgress(achievementCriteria
, GetPlayer()->getLevel());
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
)
843 if(uint32 skillvalue
= GetPlayer()->GetBaseSkillValue(achievementCriteria
->reach_skill_level
.skillID
))
844 SetCriteriaProgress(achievementCriteria
, skillvalue
);
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
)
850 if(uint32 maxSkillvalue
= GetPlayer()->GetPureMaxSkillValue(achievementCriteria
->learn_skill_level
.skillID
))
851 SetCriteriaProgress(achievementCriteria
, maxSkillvalue
);
853 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
854 if(m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end())
855 SetCriteriaProgress(achievementCriteria
, 1);
857 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
860 for(QuestStatusMap::const_iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); ++itr
)
861 if(itr
->second
.m_rewarded
)
863 SetCriteriaProgress(achievementCriteria
, counter
);
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
)
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
)
879 SetCriteriaProgress(achievementCriteria
, counter
);
882 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
883 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
886 if(GetPlayer()->GetMapId() != achievementCriteria
->complete_battleground
.mapID
)
888 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
890 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
891 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
894 if(GetPlayer()->GetMapId() != achievementCriteria
->death_at_map
.mapID
)
896 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
898 case ACHIEVEMENT_CRITERIA_TYPE_DEATH
:
900 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
903 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
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
)
919 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
922 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
:
924 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
928 Map
const* map
= GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr
.FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
929 if(!map
|| !map
->IsDungeon())
934 for(int j
= 0; achievIdForDangeon
[j
][0]; ++j
)
936 if(achievIdForDangeon
[j
][0] == achievement
->ID
)
940 // if raid accepted (ignore difficulty)
941 if(!achievIdForDangeon
[j
][2])
944 else if(GetPlayer()->GetDungeonDifficulty()==DUNGEON_DIFFICULTY_NORMAL
)
946 // dungeon in normal mode accepted
947 if(!achievIdForDangeon
[j
][1])
952 // dungeon in heroic mode accepted
953 if(!achievIdForDangeon
[j
][3])
964 //FIXME: work only for instances where max==min for players
965 if (map
->GetMaxPlayers() != achievementCriteria
->death_in_dungeon
.manLimit
)
967 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
971 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
972 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
975 if(miscvalue1
!= achievementCriteria
->killed_by_creature
.creatureEntry
)
977 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
979 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
980 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
984 // if team check required: must kill by opposition faction
985 if(achievement
->ID
==318 && miscvalue2
==GetPlayer()->GetTeam())
988 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
990 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
992 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
996 // those requirements couldn't be found in the dbc
997 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
998 if(!data
|| !data
->Meets(GetPlayer(),unit
))
1001 // miscvalue1 is the ingame fallheight*100 as stored in dbc
1002 SetCriteriaProgress(achievementCriteria
, miscvalue1
);
1005 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
:
1006 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1009 if(miscvalue2
!= achievementCriteria
->death_from
.type
)
1011 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1013 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
1015 // if miscvalues != 0, it contains the questID.
1018 if (miscvalue1
!= achievementCriteria
->complete_quest
.questID
)
1024 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria
->complete_quest
.questID
))
1028 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1029 switch(achievement
->ID
)
1038 // those requirements couldn't be found in the dbc
1039 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
1040 if(!data
|| !data
->Meets(GetPlayer(),unit
))
1049 SetCriteriaProgress(achievementCriteria
, 1);
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
)
1058 // those requirements couldn't be found in the dbc
1059 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
1063 if(!data
->Meets(GetPlayer(),unit
))
1066 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1069 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
1070 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
1072 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
1075 // those requirements couldn't be found in the dbc
1076 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
1080 if(!data
->Meets(GetPlayer(),unit
))
1083 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1086 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
1087 if(miscvalue1
&& miscvalue1
!=achievementCriteria
->learn_spell
.spellID
)
1090 if(GetPlayer()->HasSpell(achievementCriteria
->learn_spell
.spellID
))
1091 SetCriteriaProgress(achievementCriteria
, 1);
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
)
1099 if (miscvalue1
!= achievementCriteria
->loot_type
.lootType
)
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
))
1111 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
1114 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
1115 // speedup for non-login case
1116 if(miscvalue1
&& achievementCriteria
->own_item
.itemID
!= miscvalue1
)
1118 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetItemCount(achievementCriteria
->own_item
.itemID
, true));
1120 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
:
1121 // miscvalue1 contains the personal rating
1122 if (!miscvalue1
) // no update at login
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);
1138 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1140 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
1141 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1144 if(achievementCriteria
->use_item
.itemID
!= miscvalue1
)
1146 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1148 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
1149 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1152 if(miscvalue1
!= achievementCriteria
->own_item
.itemID
)
1154 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
1156 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
1158 WorldMapOverlayEntry
const* worldOverlayEntry
= sWorldMapOverlayStore
.LookupEntry(achievementCriteria
->explore_area
.areaReference
);
1159 if(!worldOverlayEntry
)
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
1169 int32 exploreFlag
= GetAreaFlagByAreaID(area_id
);
1173 uint32 playerIndexOffset
= uint32(exploreFlag
) / 32;
1174 uint32 mask
= 1<< (uint32(exploreFlag
) % 32);
1176 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ playerIndexOffset
) & mask
)
1184 SetCriteriaProgress(achievementCriteria
, 1);
1187 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
1188 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetBankBagSlotCount());
1190 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
1192 // skip faction check only at loading
1193 if (miscvalue1
&& miscvalue1
!= achievementCriteria
->gain_reputation
.factionID
)
1196 int32 reputation
= GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria
->gain_reputation
.factionID
);
1198 SetCriteriaProgress(achievementCriteria
, reputation
);
1201 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
1203 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1206 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
1208 // skip for login case
1211 SetCriteriaProgress(achievementCriteria
, 1);
1214 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
:
1216 // miscvalue1 = equip_slot+1 (for avoid use 0)
1219 uint32 item_slot
= miscvalue1
-1;
1220 if(item_slot
!= achievementCriteria
->equip_epic_item
.itemSlot
)
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
))
1226 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_SET
);
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
1236 if(miscvalue2
!= achievementCriteria
->roll_greed_on_loot
.rollValue
)
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
)
1246 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1249 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
1251 // miscvalue1 = emote
1254 if(miscvalue1
!= achievementCriteria
->do_emote
.emoteID
)
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
))
1264 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1267 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
:
1268 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
:
1273 if (achievementCriteria
->healing_done
.flag
== ACHIEVEMENT_CRITERIA_CONDITION_MAP
)
1275 if(GetPlayer()->GetMapId() != achievementCriteria
->healing_done
.mapid
)
1278 // map specific case (BG in fact) expected player targeted damage/heal
1279 if(!unit
|| unit
->GetTypeId()!=TYPEID_PLAYER
)
1283 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
1286 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
:
1287 // miscvalue1 = item_id
1290 if (miscvalue1
!= achievementCriteria
->equip_item
.itemID
)
1293 SetCriteriaProgress(achievementCriteria
, 1);
1295 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT
:
1296 // miscvalue1 = go entry
1299 if (miscvalue1
!= achievementCriteria
->use_gameobject
.goEntry
)
1302 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1304 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL
:
1306 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1310 // those requirements couldn't be found in the dbc
1311 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
1315 if (!data
->Meets(GetPlayer(),unit
))
1318 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1321 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT
:
1324 if (miscvalue1
!= achievementCriteria
->fish_in_gameobject
.goEntry
)
1327 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1329 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
1331 if (miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skillline_spell
.skillLine
)
1334 uint32 spellCount
= 0;
1335 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
1336 spellIter
!= GetPlayer()->GetSpellMap().end();
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
)
1346 SetCriteriaProgress(achievementCriteria
, spellCount
);
1349 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL
:
1350 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1354 if (achievementCriteria
->win_duel
.duelCount
)
1356 // those requirements couldn't be found in the dbc
1357 AchievementCriteriaRequirementSet
const* data
= sAchievementMgr
.GetCriteriaRequirementSet(achievementCriteria
);
1361 if (!data
->Meets(GetPlayer(),unit
))
1365 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1367 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION
:
1368 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1370 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION
:
1371 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1373 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS
:
1374 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
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
1382 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(miscvalue1
);
1383 if (!proto
|| proto
->Quality
< ITEM_QUALITY_EPIC
)
1385 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1388 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE
:
1390 if (miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skill_line
.skillLine
)
1393 uint32 spellCount
= 0;
1394 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
1395 spellIter
!= GetPlayer()->GetSpellMap().end();
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
)
1403 SetCriteriaProgress(achievementCriteria
, spellCount
);
1406 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL
:
1407 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
));
1409 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS
:
1410 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->hk_class
.classID
)
1413 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1415 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE
:
1416 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->hk_race
.raceID
)
1419 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1421 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED
:
1422 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetMoney(), PROGRESS_HIGHEST
);
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
:
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
)
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
))
1486 CriteriaProgressMap::const_iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
1487 if(itr
== m_criteriaProgress
.end())
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())
1505 // skip wrong race achievements
1506 for(int i
= 1; i
< MAX_RACES
; ++i
)
1507 if(achievIdByRace
[i
] == achievement
->ID
&& i
!= GetPlayer()->getRace())
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
:
1630 void AchievementMgr::CompletedCriteriaFor(AchievementEntry
const* achievement
)
1632 // counter can never complete
1633 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
1636 // already completed and stored
1637 if (m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
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
)
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
);
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())
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
)
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
1693 completed_all
= false;
1695 // completed as have req. count of completed criterias
1696 if(achievementForTestCount
> 0 && achievementForTestCount
<= count
)
1700 // all criterias completed requirement
1701 if(completed_all
&& achievementForTestCount
==0)
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)
1721 progress
= &m_criteriaProgress
[entry
->ID
];
1722 progress
->counter
= changeValue
;
1723 progress
->date
= time(NULL
);
1727 progress
= &iter
->second
;
1729 uint32 newValue
= 0;
1733 newValue
= changeValue
;
1735 case PROGRESS_ACCUMULATE
:
1738 uint32 max_value
= std::numeric_limits
<uint32
>::max();
1739 newValue
= max_value
- progress
->counter
> changeValue
? progress
->counter
+ changeValue
: max_value
;
1742 case PROGRESS_HIGHEST
:
1743 newValue
= progress
->counter
< changeValue
? changeValue
: progress
->counter
;
1747 // not update (not mark as changed) if counter will have same value
1748 if(progress
->counter
== newValue
)
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())
1774 SendAchievementEarned(achievement
);
1775 CompletedAchievementData
& ca
= m_completedAchievements
[achievement
->ID
];
1776 ca
.date
= time(NULL
);
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());
1794 if(uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
? 0 : 1])
1796 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
1797 GetPlayer()->SetTitle(titleEntry
);
1803 Item
* item
= reward
->itemId
? Item::CreateItem(reward
->itemId
,1,GetPlayer ()) : NULL
;
1805 int loc_idx
= GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1808 std::string subject
= reward
->subject
;
1809 std::string text
= reward
->text
;
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
);
1825 // save new item before send
1826 item
->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
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
));
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();
1871 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
1879 //==========================================================
1880 AchievementCriteriaEntryList
const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type
)
1882 return m_AchievementCriteriasByType
[type
];
1885 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1887 if(sAchievementCriteriaStore
.GetNumRows()==0)
1893 sLog
.outErrorDb(">> Loaded 0 achievement criteria.");
1897 barGoLink
bar( sAchievementCriteriaStore
.GetNumRows() );
1898 for (uint32 entryId
= 0; entryId
< sAchievementCriteriaStore
.GetNumRows(); ++entryId
)
1902 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
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
);
1913 sLog
.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType
->size());
1916 void AchievementGlobalMgr::LoadAchievementReferenceList()
1918 if(sAchievementStore
.GetNumRows()==0)
1924 sLog
.outErrorDb(">> Loaded 0 achievement references.");
1929 barGoLink
bar( sAchievementStore
.GetNumRows() );
1930 for (uint32 entryId
= 0; entryId
< sAchievementStore
.GetNumRows(); ++entryId
)
1934 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(entryId
);
1935 if(!achievement
|| !achievement
->refAchievement
)
1938 m_AchievementListByReferencedId
[achievement
->refAchievement
].push_back(achievement
);
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");
1958 sLog
.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_requirement` is empty.");
1963 uint32 disabled_count
= 0;
1964 barGoLink
bar((int)result
->GetRowCount());
1968 Field
*fields
= result
->Fetch();
1969 uint32 criteria_id
= fields
[0].GetUInt32();
1971 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(criteria_id
);
1975 sLog
.outErrorDb( "Table `achievement_criteria_requirement`.`criteria_id` %u does not exist, ignoring.", criteria_id
);
1979 AchievementCriteriaRequirement
data(fields
[1].GetUInt32(),fields
[2].GetUInt32(),fields
[3].GetUInt32());
1981 if (!data
.IsValid(criteria
))
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
)
1994 // add real data only for not NONE requirements
1995 if (data
.requirementType
!= ACHIEVEMENT_CRITERIA_REQUIRE_NONE
)
1998 // counting requirements
2000 } while(result
->NextRow());
2004 // post loading checks
2005 for (uint32 entryId
= 0; entryId
< sAchievementCriteriaStore
.GetNumRows(); ++entryId
)
2007 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
2011 switch(criteria
->requiredType
)
2013 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG
:
2014 if(!criteria
->win_bg
.additionalRequirement1_type
&& !criteria
->win_bg
.additionalRequirement2_type
)
2017 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
2019 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
2021 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(criteria
->referredAchievement
);
2025 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
2026 switch(achievement
->ID
)
2039 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
2041 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
: // any cases
2043 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
: // need skip generic cases
2044 if(criteria
->win_rated_arena
.flag
!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE
)
2047 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
: // any cases
2049 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
: // need skip generic cases
2050 if(criteria
->do_emote
.count
==0)
2053 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL
:// any cases
2055 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL
: // skip statistics
2056 if(criteria
->win_duel
.duelCount
==0)
2059 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
: // any cases
2061 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
: // need skip generic cases
2062 if(criteria
->loot_type
.lootTypeCount
!=1)
2065 default: // type not use DB data, ignore
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
);
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");
2087 sLog
.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
2091 barGoLink
bar((int)result
->GetRowCount());
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
);
2106 m_allCompletedAchievements
.insert(achievement_id
);
2107 } while(result
->NextRow());
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
2120 QueryResult
*result
= WorldDatabase
.Query("SELECT entry, gender, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2129 sLog
.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2134 barGoLink
bar((int)result
->GetRowCount());
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
);
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
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
)
2170 sLog
.outErrorDb( "Table `achievement_reward` must have single GENDER_NONE (%u) case (Entry: %u), ignore duplicate case", GENDER_NONE
, entry
);
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
);
2187 if (reward
.titleId
[0])
2189 CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(reward
.titleId
[0]);
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]);
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
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
);
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
);
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
);
2237 m_achievementRewards
.insert(AchievementRewards::value_type(entry
,reward
));
2240 } while (result
->NextRow());
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");
2261 sLog
.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2265 barGoLink
bar((int)result
->GetRowCount());
2269 Field
*fields
= result
->Fetch();
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
);
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
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
)
2297 sLog
.outErrorDb( "Table `locales_achievement_reward` must have single GENDER_NONE (%u) case (Entry: %u), ignore duplicate case", GENDER_NONE
, entry
);
2304 for(int i
= 1; i
< MAX_LOCALE
; ++i
)
2306 std::string str
= fields
[2+2*(i
-1)].GetCppString();
2309 int idx
= sObjectMgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
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();
2321 int idx
= sObjectMgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
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());
2339 sLog
.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales
.size() );