2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "AchievementMgr.h"
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"
32 #include "GridNotifiersImpl.h"
36 #include "Policies/SingletonImp.h"
38 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr
);
42 class AchievementChatBuilder
45 AchievementChatBuilder(Player
const& pl
, ChatMsg msgtype
, int32 textId
, uint32 ach_id
)
46 : i_player(pl
), i_msgtype(msgtype
), i_textId(textId
), i_achievementId(ach_id
) {}
47 void operator()(WorldPacket
& data
, int32 loc_idx
)
49 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
51 data
<< uint8(i_msgtype
);
52 data
<< uint32(LANG_UNIVERSAL
);
53 data
<< uint64(i_player
.GetGUID());
55 data
<< uint64(i_player
.GetGUID());
56 data
<< uint32(strlen(text
)+1);
59 data
<< uint32(i_achievementId
);
63 Player
const& i_player
;
66 uint32 i_achievementId
;
71 bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry
const* criteria
)
73 if(dataType
>= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE
)
75 sLog
.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria
->ID
,dataType
);
79 switch(criteria
->requiredType
)
81 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
:
82 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
83 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
:
84 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
87 sLog
.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria
->ID
, criteria
->requiredType
);
93 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE
:
94 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE
:
96 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE
:
97 if(!creature
.id
|| !objmgr
.GetCreatureTemplate(creature
.id
))
99 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed creature id in value1 (%u), ignore.",
100 criteria
->ID
, criteria
->requiredType
,dataType
,creature
.id
);
104 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE
:
105 if(!classRace
.class_id
&& !classRace
.race_id
)
107 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must have not 0 in one from value fields, ignore.",
108 criteria
->ID
, criteria
->requiredType
,dataType
);
111 if(classRace
.class_id
&& ((1 << (classRace
.class_id
-1)) & CLASSMASK_ALL_PLAYABLE
)==0)
113 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed class in value1 (%u), ignore.",
114 criteria
->ID
, criteria
->requiredType
,dataType
,classRace
.class_id
);
117 if(classRace
.race_id
&& ((1 << (classRace
.race_id
-1)) & RACEMASK_ALL_PLAYABLE
)==0)
119 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed race in value2 (%u), ignore.",
120 criteria
->ID
, criteria
->requiredType
,dataType
,classRace
.race_id
);
124 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH
:
125 if(health
.percent
< 1 || health
.percent
> 100)
127 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) have wrong percent value in value1 (%u), ignore.",
128 criteria
->ID
, criteria
->requiredType
,dataType
,health
.percent
);
132 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD
:
133 if(player_dead
.own_team_flag
> 1)
135 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong boolean value1 (%u).",
136 criteria
->ID
, criteria
->requiredType
,dataType
,player_dead
.own_team_flag
);
140 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA
:
141 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA
:
143 SpellEntry
const* spellEntry
= sSpellStore
.LookupEntry(aura
.spell_id
);
146 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.",
147 criteria
->ID
, criteria
->requiredType
,(dataType
==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA
?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType
,aura
.spell_id
);
150 if(aura
.effect_idx
>= 3)
152 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.",
153 criteria
->ID
, criteria
->requiredType
,(dataType
==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA
?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType
,aura
.effect_idx
);
156 if(!spellEntry
->EffectApplyAuraName
[aura
.effect_idx
])
158 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
159 criteria
->ID
, criteria
->requiredType
,(dataType
==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA
?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType
,aura
.spell_id
,aura
.effect_idx
);
164 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA
:
165 if(!GetAreaEntryByAreaID(area
.id
))
167 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) have wrong area id in value1 (%u), ignore.",
168 criteria
->ID
, criteria
->requiredType
,dataType
,area
.id
);
173 sLog
.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria
->ID
, criteria
->requiredType
,dataType
);
179 bool AchievementCriteriaData::Meets(Player
const* source
, Unit
const* target
, uint32 miscvalue1
/*= 0*/) const
183 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE
:
185 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE
:
186 if (!target
|| target
->GetTypeId()!=TYPEID_UNIT
)
188 if (target
->GetEntry() != creature
.id
)
191 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE
:
192 if (!target
|| target
->GetTypeId()!=TYPEID_PLAYER
)
194 if(classRace
.class_id
&& classRace
.class_id
!= ((Player
*)target
)->getClass())
196 if(classRace
.race_id
&& classRace
.race_id
!= ((Player
*)target
)->getRace())
199 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH
:
200 if (!target
|| target
->GetTypeId()!=TYPEID_PLAYER
)
202 return target
->GetHealth()*100 <= health
.percent
*target
->GetMaxHealth();
203 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD
:
204 if (!target
|| target
->GetTypeId() != TYPEID_PLAYER
|| target
->isAlive() || ((Player
*)target
)->GetDeathTimer() == 0)
206 // flag set == must be same team, not set == different team
207 return (((Player
*)target
)->GetTeam() == source
->GetTeam()) == (player_dead
.own_team_flag
!= 0);
208 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA
:
209 return source
->HasAura(aura
.spell_id
,aura
.effect_idx
);
210 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA
:
212 uint32 zone_id
,area_id
;
213 source
->GetZoneAndAreaId(zone_id
,area_id
);
214 return area
.id
==zone_id
|| area
.id
==area_id
;
216 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA
:
217 return target
&& target
->HasAura(aura
.spell_id
,aura
.effect_idx
);
218 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE
:
219 return miscvalue1
>= value
.minvalue
;
225 bool AchievementCriteriaDataSet::Meets(Player
const* source
, Unit
const* target
, uint32 miscvalue
/*= 0*/) const
227 for(Storage::const_iterator itr
= storage
.begin(); itr
!= storage
.end(); ++itr
)
228 if(!itr
->Meets(source
,target
,miscvalue
))
234 AchievementMgr::AchievementMgr(Player
*player
)
239 AchievementMgr::~AchievementMgr()
243 void AchievementMgr::Reset()
245 for(CompletedAchievementMap::const_iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
247 WorldPacket
data(SMSG_ACHIEVEMENT_DELETED
,4);
248 data
<< uint32(iter
->first
);
249 m_player
->SendDirectMessage(&data
);
252 for(CriteriaProgressMap::const_iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
254 WorldPacket
data(SMSG_CRITERIA_DELETED
,4);
255 data
<< uint32(iter
->first
);
256 m_player
->SendDirectMessage(&data
);
259 m_completedAchievements
.clear();
260 m_criteriaProgress
.clear();
261 DeleteFromDB(m_player
->GetGUIDLow());
264 CheckAllAchievementCriteria();
267 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
, uint32 miscvalue2
)
269 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
270 sLog
.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type
, miscvalue1
, miscvalue2
);
272 if (!sWorld
.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS
) && m_player
->GetSession()->GetSecurity() > SEC_PLAYER
)
275 AchievementCriteriaEntryList
const& achievementCriteriaList
= achievementmgr
.GetAchievementCriteriaByType(type
);
276 for(AchievementCriteriaEntryList::const_iterator i
= achievementCriteriaList
.begin(); i
!=achievementCriteriaList
.end(); ++i
)
278 AchievementCriteriaEntry
const *achievementCriteria
= (*i
);
280 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
284 // don't update already completed criteria
285 if (IsCompletedCriteria(achievementCriteria
,achievement
))
290 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
: // have total statistic also not expected to be reset
291 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
: // have total statistic also not expected to be reset
292 if (achievementCriteria
->healing_done
.flag
== miscvalue1
&&
293 achievementCriteria
->healing_done
.mapid
== miscvalue2
)
294 SetCriteriaProgress(achievementCriteria
, 0, PROGRESS_SET
);
296 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
: // have total statistic also not expected to be reset
297 // reset only the criteria having the miscvalue1 condition
298 if (achievementCriteria
->win_rated_arena
.flag
== miscvalue1
)
299 SetCriteriaProgress(achievementCriteria
, 0, PROGRESS_SET
);
301 default: // reset all cases
307 void AchievementMgr::DeleteFromDB(uint32 lowguid
)
309 CharacterDatabase
.BeginTransaction ();
310 CharacterDatabase
.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid
);
311 CharacterDatabase
.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid
);
312 CharacterDatabase
.CommitTransaction ();
315 void AchievementMgr::SaveToDB()
317 if(!m_completedAchievements
.empty())
319 bool need_execute
= false;
320 std::ostringstream ssdel
;
321 std::ostringstream ssins
;
322 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
324 if(!iter
->second
.changed
)
327 /// first new/changed record prefix
330 ssdel
<< "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
331 ssins
<< "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
334 /// next new/changed record prefix
341 // new/changed record data
342 ssdel
<< iter
->first
;
343 ssins
<< "("<<GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << uint64(iter
->second
.date
) << ")";
345 /// mark as saved in db
346 iter
->second
.changed
= false;
354 CharacterDatabase
.Execute( ssdel
.str().c_str() );
355 CharacterDatabase
.Execute( ssins
.str().c_str() );
359 if(!m_criteriaProgress
.empty())
361 /// prepare deleting and insert
362 bool need_execute_del
= false;
363 bool need_execute_ins
= false;
364 std::ostringstream ssdel
;
365 std::ostringstream ssins
;
366 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
368 if(!iter
->second
.changed
)
371 // deleted data (including 0 progress state)
373 /// first new/changed record prefix (for any counter value)
374 if(!need_execute_del
)
376 ssdel
<< "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
377 need_execute_del
= true;
379 /// next new/changed record prefix
383 // new/changed record data
384 ssdel
<< iter
->first
;
387 // store data only for real progress
388 if(iter
->second
.counter
!= 0)
390 /// first new/changed record prefix
391 if(!need_execute_ins
)
393 ssins
<< "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
394 need_execute_ins
= true;
396 /// next new/changed record prefix
400 // new/changed record data
401 ssins
<< "(" << GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << iter
->second
.counter
<< ", " << iter
->second
.date
<< ")";
404 /// mark as updated in db
405 iter
->second
.changed
= false;
408 if(need_execute_del
) // DELETE ... IN (.... _)_
411 if(need_execute_del
|| need_execute_ins
)
414 CharacterDatabase
.Execute( ssdel
.str().c_str() );
416 CharacterDatabase
.Execute( ssins
.str().c_str() );
421 void AchievementMgr::LoadFromDB(QueryResult
*achievementResult
, QueryResult
*criteriaResult
)
423 if(achievementResult
)
427 Field
*fields
= achievementResult
->Fetch();
429 uint32 achievement_id
= fields
[0].GetUInt32();
431 // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
432 if(!sAchievementStore
.LookupEntry(achievement_id
))
435 CompletedAchievementData
& ca
= m_completedAchievements
[achievement_id
];
436 ca
.date
= time_t(fields
[1].GetUInt64());
438 } while(achievementResult
->NextRow());
439 delete achievementResult
;
446 Field
*fields
= criteriaResult
->Fetch();
448 uint32 id
= fields
[0].GetUInt32();
449 uint32 counter
= fields
[1].GetUInt32();
450 time_t date
= time_t(fields
[2].GetUInt64());
452 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(id
);
455 // we will remove not existed criteria for all characters
456 sLog
.outError("Not existed achievement creataria %u data removed from table `character_achievement_progress`.",id
);
457 CharacterDatabase
.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id
);
461 if (criteria
->timeLimit
&& time_t(date
+ criteria
->timeLimit
) < time(NULL
))
464 CriteriaProgress
& progress
= m_criteriaProgress
[id
];
465 progress
.counter
= counter
;
466 progress
.date
= date
;
467 progress
.changed
= false;
468 } while(criteriaResult
->NextRow());
469 delete criteriaResult
;
474 void AchievementMgr::SendAchievementEarned(AchievementEntry
const* achievement
)
476 if(GetPlayer()->GetSession()->PlayerLoading())
480 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
481 sLog
.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement
->ID
);
484 if(Guild
* guild
= objmgr
.GetGuildById(GetPlayer()->GetGuildId()))
486 MaNGOS::AchievementChatBuilder
say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT
, LANG_ACHIEVEMENT_EARNED
,achievement
->ID
);
487 MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> say_do(say_builder
);
488 guild
->BroadcastWorker(say_do
,GetPlayer());
491 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_KILL
|ACHIEVEMENT_FLAG_REALM_FIRST_REACH
))
493 // broadcast realm first reached
494 WorldPacket
data(SMSG_SERVER_FIRST_ACHIEVEMENT
, strlen(GetPlayer()->GetName())+1+8+4+4);
495 data
<< GetPlayer()->GetName();
496 data
<< uint64(GetPlayer()->GetGUID());
497 data
<< uint32(achievement
->ID
);
498 data
<< uint32(0); // 1=link supplied string as player name, 0=display plain string
499 sWorld
.SendGlobalMessage(&data
);
503 CellPair p
= MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
506 cell
.data
.Part
.reserved
= ALL_DISTRICT
;
509 MaNGOS::AchievementChatBuilder
say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT
, LANG_ACHIEVEMENT_EARNED
,achievement
->ID
);
510 MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> say_do(say_builder
);
511 MaNGOS::PlayerDistWorker
<MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> > say_worker(GetPlayer(),sWorld
.getConfig(CONFIG_LISTEN_RANGE_SAY
),say_do
);
512 TypeContainerVisitor
<MaNGOS::PlayerDistWorker
<MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> >, WorldTypeMapContainer
> message(say_worker
);
513 CellLock
<GridReadGuard
> cell_lock(cell
, p
);
514 cell_lock
->Visit(cell_lock
, message
, *GetPlayer()->GetMap());
517 WorldPacket
data(SMSG_ACHIEVEMENT_EARNED
, 8+4+8);
518 data
.append(GetPlayer()->GetPackGUID());
519 data
<< uint32(achievement
->ID
);
520 data
<< uint32(secsToTimeBitFields(time(NULL
)));
522 GetPlayer()->SendMessageToSetInRange(&data
, sWorld
.getConfig(CONFIG_LISTEN_RANGE_SAY
), true);
525 void AchievementMgr::SendCriteriaUpdate(uint32 id
, CriteriaProgress
const* progress
)
527 WorldPacket
data(SMSG_CRITERIA_UPDATE
, 8+4+8);
530 // the counter is packed like a packed Guid
531 data
.appendPackGUID(progress
->counter
);
533 data
.append(GetPlayer()->GetPackGUID());
535 data
<< uint32(secsToTimeBitFields(progress
->date
));
536 data
<< uint32(0); // timer 1
537 data
<< uint32(0); // timer 2
538 GetPlayer()->SendDirectMessage(&data
);
542 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
544 void AchievementMgr::CheckAllAchievementCriteria()
546 // suppress sending packets
547 for(uint32 i
=0; i
<ACHIEVEMENT_CRITERIA_TYPE_TOTAL
; ++i
)
548 UpdateAchievementCriteria(AchievementCriteriaTypes(i
));
551 static const uint32 achievIdByArenaSlot
[MAX_ARENA_SLOT
] = { 1057, 1107, 1108 };
552 static const uint32 achievIdForDangeon
[][4] =
554 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
555 { 321, true, true, true },
556 { 916, false, true, false },
557 { 917, false, true, false },
558 { 918, true, false, false },
559 { 2219, false, false, true },
560 { 0, false, false, false }
564 * this function will be called whenever the user might have done a criteria relevant action
566 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
, uint32 miscvalue2
, Unit
*unit
, uint32 time
)
568 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
569 sLog
.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type
, miscvalue1
, miscvalue2
, time
);
571 if (!sWorld
.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS
) && m_player
->GetSession()->GetSecurity() > SEC_PLAYER
)
574 AchievementCriteriaEntryList
const& achievementCriteriaList
= achievementmgr
.GetAchievementCriteriaByType(type
);
575 for(AchievementCriteriaEntryList::const_iterator i
= achievementCriteriaList
.begin(); i
!=achievementCriteriaList
.end(); ++i
)
577 AchievementCriteriaEntry
const *achievementCriteria
= (*i
);
579 if (achievementCriteria
->groupFlag
& ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP
&& GetPlayer()->GetGroup())
582 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
586 if ((achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_HORDE
&& GetPlayer()->GetTeam() != HORDE
) ||
587 (achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_ALLIANCE
&& GetPlayer()->GetTeam() != ALLIANCE
))
590 // don't update already completed criteria
591 if (IsCompletedCriteria(achievementCriteria
,achievement
))
596 // std. case: increment at 1
597 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
598 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS
:
599 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL
:
600 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL
:
601 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED
:
602 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED
:
603 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED
:
604 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN
:
605 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS
:
606 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
609 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
611 // std case: increment at miscvalue1
612 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS
:
613 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
:
614 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING
:
615 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER
:
616 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL
:
617 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
:
618 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED
:
619 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED
:
620 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
623 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
625 // std case: high value at miscvalue1
626 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID
:
627 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD
: /* FIXME: for online player only currently */
628 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT
:
629 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED
:
630 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED
:
631 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED
:
632 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
635 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_HIGHEST
);
640 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
641 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
644 if(achievementCriteria
->kill_creature
.creatureID
!= miscvalue1
)
647 // LOT achievement->ID required special custom checks
648 switch(achievement
->ID
)
651 case 489: case 490: case 491: case 492: case 493: case 494: case 495:
652 case 496: case 497: case 498: case 499: case 500: case 563: case 565:
653 case 567: case 569: case 573: case 575: case 577: case 623: case 625:
654 case 667: case 668: case 669: case 670: case 671: case 672: case 673:
655 case 674: case 675: case 676: case 677: case 678: case 679: case 680:
656 case 681: case 682: case 1367: case 1368: case 1378: case 1379:
657 case 1380: case 1381: case 1382: case 1383: case 1384: case 1385:
658 case 1386: case 1387: case 1388: case 1389: case 1390: case 1393:
659 case 1394: case 1400: case 1402: case 1504: case 1505: case 1506:
660 case 1507: case 1508: case 1509: case 1510: case 1511: case 1512:
661 case 1513: case 1514: case 1515: case 1721: case 1754: case 1756:
662 case 1768: case 1817: case 1865:
663 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC
)
667 case 579: case 1296: case 1297: case 1816: case 1834: case 1857: case 1859:
668 case 1860: case 1861: case 1862: case 1864: case 1866: case 1867: case 1868:
669 case 1870: case 1871: case 1872: case 1873: case 1875: case 1877: case 1919:
670 case 2036: case 2037: case 2038: case 2039: case 2040: case 2041: case 2042:
671 case 2043: case 2044: case 2045: case 2046: case 2048: case 2052: case 2053:
672 case 2054: case 2056: case 2057: case 2058: case 2139: case 2140: case 2147:
673 case 2149: case 2150: case 2151: case 2152: case 2154: case 2155: case 2156:
674 case 2157: case 2179: case 2181: case 2183: case 2185: case 2186:
675 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC
)
677 // FIX ME: mark as fail always until implement
680 case 578: case 624: case 1790: case 1856: case 1858: case 1869: case 1874:
681 case 1996: case 1997: case 2047: case 2049: case 2050: case 2051: case 2146:
682 case 2148: case 2153: case 2178: case 2180: case 2182: case 2184: case 2187:
683 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL
)
685 // FIX ME: mark as fail always until implement
689 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL
)
694 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
696 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
697 SetCriteriaProgress(achievementCriteria
, GetPlayer()->getLevel());
699 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
700 // update at loading or specific skill update
701 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->reach_skill_level
.skillID
)
703 if(uint32 skillvalue
= GetPlayer()->GetBaseSkillValue(achievementCriteria
->reach_skill_level
.skillID
))
704 SetCriteriaProgress(achievementCriteria
, skillvalue
);
706 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
:
707 // update at loading or specific skill update
708 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skill_level
.skillID
)
710 if(uint32 maxSkillvalue
= GetPlayer()->GetPureMaxSkillValue(achievementCriteria
->learn_skill_level
.skillID
))
711 SetCriteriaProgress(achievementCriteria
, maxSkillvalue
);
713 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
714 if(m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end())
715 SetCriteriaProgress(achievementCriteria
, 1);
717 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
720 for(QuestStatusMap::const_iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
721 if(itr
->second
.m_rewarded
)
723 SetCriteriaProgress(achievementCriteria
, counter
);
726 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
728 // speedup for non-login case
729 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->complete_quests_in_zone
.zoneID
)
733 for(QuestStatusMap::const_iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
735 Quest
const* quest
= objmgr
.GetQuestTemplate(itr
->first
);
736 if(itr
->second
.m_rewarded
&& quest
->GetZoneOrSort() >= 0 && uint32(quest
->GetZoneOrSort()) == achievementCriteria
->complete_quests_in_zone
.zoneID
)
739 SetCriteriaProgress(achievementCriteria
, counter
);
742 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
743 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
746 if(GetPlayer()->GetMapId() != achievementCriteria
->complete_battleground
.mapID
)
748 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
750 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
751 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
754 if(GetPlayer()->GetMapId() != achievementCriteria
->death_at_map
.mapID
)
756 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
758 case ACHIEVEMENT_CRITERIA_TYPE_DEATH
:
760 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
763 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
765 for(int j
= 0; j
< MAX_ARENA_SLOT
; ++j
)
767 if(achievIdByArenaSlot
[j
] == achievement
->ID
)
769 BattleGround
* bg
= GetPlayer()->GetBattleGround();
770 if(!bg
|| !bg
->isArena() || ArenaTeam::GetSlotByType(bg
->GetArenaType()) != j
)
779 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
782 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
:
784 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
788 Map
const* map
= GetPlayer()->GetMap();
789 if(!map
->IsDungeon())
794 for(int j
= 0; achievIdForDangeon
[j
][0]; ++j
)
796 if(achievIdForDangeon
[j
][0] == achievement
->ID
)
800 // if raid accepted (ignore difficulty)
801 if(!achievIdForDangeon
[j
][2])
804 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL
)
806 // dungeon in normal mode accepted
807 if(!achievIdForDangeon
[j
][1])
812 // dungeon in heroic mode accepted
813 if(!achievIdForDangeon
[j
][3])
824 //FIXME: work only for instances where max==min for players
825 if(((InstanceMap
*)map
)->GetMaxPlayers() != achievementCriteria
->death_in_dungeon
.manLimit
)
827 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
831 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
832 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
835 if(miscvalue1
!= achievementCriteria
->killed_by_creature
.creatureEntry
)
837 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
839 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
840 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
844 // if team check required: must kill by opposition faction
845 if(achievement
->ID
==318 && miscvalue2
==GetPlayer()->GetTeam())
848 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
850 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
852 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
855 if(achievement
->ID
== 1260)
857 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED
)
859 if(!IsHolidayActive(HOLIDAY_BREWFEST
))
862 // miscvalue1 is the ingame fallheight*100 as stored in dbc
863 SetCriteriaProgress(achievementCriteria
, miscvalue1
);
866 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
:
867 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
870 if(miscvalue2
!= achievementCriteria
->death_from
.type
)
872 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
874 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
875 // if miscvalues != 0, it contains the questID.
878 if (miscvalue1
== achievementCriteria
->complete_quest
.questID
)
879 SetCriteriaProgress(achievementCriteria
, 1);
884 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria
->complete_quest
.questID
))
885 SetCriteriaProgress(achievementCriteria
, 1);
888 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
889 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
890 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->be_spell_target
.spellID
)
892 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
894 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
895 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
897 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
899 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
900 if(miscvalue1
&& miscvalue1
!=achievementCriteria
->learn_spell
.spellID
)
903 if(GetPlayer()->HasSpell(achievementCriteria
->learn_spell
.spellID
))
904 SetCriteriaProgress(achievementCriteria
, 1);
906 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
:
908 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
909 // miscvalue2=count of item loot
910 if (!miscvalue1
|| !miscvalue2
)
912 if (miscvalue1
!= achievementCriteria
->loot_type
.lootType
)
916 if(achievementCriteria
->loot_type
.lootTypeCount
==1)
918 // those requirements couldn't be found in the dbc
919 AchievementCriteriaDataSet
const* data
= achievementmgr
.GetCriteriaDataSet(achievementCriteria
);
923 if(!data
->Meets(GetPlayer(),unit
))
927 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
930 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
931 // speedup for non-login case
932 if(miscvalue1
&& achievementCriteria
->own_item
.itemID
!= miscvalue1
)
934 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetItemCount(achievementCriteria
->own_item
.itemID
, true));
936 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
:
937 // miscvalue1 contains the personal rating
938 if (!miscvalue1
) // no update at login
941 // additional requirements
942 if(achievementCriteria
->win_rated_arena
.flag
==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE
)
944 // those requirements couldn't be found in the dbc
945 AchievementCriteriaDataSet
const* data
= achievementmgr
.GetCriteriaDataSet(achievementCriteria
);
946 if(!data
|| !data
->Meets(GetPlayer(),unit
,miscvalue1
))
948 // reset the progress as we have a win without the requirement.
949 SetCriteriaProgress(achievementCriteria
, 0);
954 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
956 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
957 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
960 if(achievementCriteria
->use_item
.itemID
!= miscvalue1
)
962 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
964 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
965 // You _have_ to loot that item, just owning it when logging in does _not_ count!
968 if(miscvalue1
!= achievementCriteria
->own_item
.itemID
)
970 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
972 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
974 WorldMapOverlayEntry
const* worldOverlayEntry
= sWorldMapOverlayStore
.LookupEntry(achievementCriteria
->explore_area
.areaReference
);
975 if(!worldOverlayEntry
)
978 bool matchFound
= false;
979 for (int j
= 0; j
< MAX_WORLD_MAP_OVERLAY_AREA_IDX
; ++j
)
981 uint32 area_id
= worldOverlayEntry
->areatableID
[j
];
982 if(!area_id
) // array have 0 only in empty tail
985 int32 exploreFlag
= GetAreaFlagByAreaID(area_id
);
989 uint32 playerIndexOffset
= uint32(exploreFlag
) / 32;
990 uint32 mask
= 1<< (uint32(exploreFlag
) % 32);
992 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ playerIndexOffset
) & mask
)
1000 SetCriteriaProgress(achievementCriteria
, 1);
1003 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
1004 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetBankBagSlotCount());
1006 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
1008 // skip faction check only at loading
1009 if (miscvalue1
&& miscvalue1
!= achievementCriteria
->gain_reputation
.factionID
)
1012 int32 reputation
= GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria
->gain_reputation
.factionID
);
1014 SetCriteriaProgress(achievementCriteria
, reputation
);
1017 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
1019 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1022 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
1024 // skip for login case
1027 SetCriteriaProgress(achievementCriteria
, 1);
1030 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
:
1031 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
:
1033 // miscvalue1 = itemid
1034 // miscvalue2 = diced value
1037 if(miscvalue2
!= achievementCriteria
->roll_greed_on_loot
.rollValue
)
1039 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( miscvalue1
);
1041 uint32 requiredItemLevel
= 0;
1042 if (achievementCriteria
->ID
== 2412 || achievementCriteria
->ID
== 2358)
1043 requiredItemLevel
= 185;
1045 if(!pProto
|| pProto
->ItemLevel
<requiredItemLevel
)
1047 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1050 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
1052 // miscvalue1 = emote
1055 if(miscvalue1
!= achievementCriteria
->do_emote
.emoteID
)
1057 if(achievementCriteria
->do_emote
.count
)
1059 // those requirements couldn't be found in the dbc
1060 AchievementCriteriaDataSet
const* data
= achievementmgr
.GetCriteriaDataSet(achievementCriteria
);
1064 if(!data
->Meets(GetPlayer(),unit
))
1068 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1071 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
:
1072 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
:
1077 if (achievementCriteria
->healing_done
.flag
== ACHIEVEMENT_CRITERIA_CONDITION_MAP
)
1079 if(GetPlayer()->GetMapId() != achievementCriteria
->healing_done
.mapid
)
1082 // map specific case (BG in fact) expected player targeted damage/heal
1083 if(!unit
|| unit
->GetTypeId()!=TYPEID_PLAYER
)
1087 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
1090 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
:
1091 // miscvalue1 = item_id
1094 if(miscvalue1
!= achievementCriteria
->equip_item
.itemID
)
1097 SetCriteriaProgress(achievementCriteria
, 1);
1099 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT
:
1100 // miscvalue1 = go entry
1103 if(miscvalue1
!= achievementCriteria
->use_gameobject
.goEntry
)
1106 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1108 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT
:
1111 if (miscvalue1
!= achievementCriteria
->fish_in_gameobject
.goEntry
)
1114 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1116 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
1118 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skillline_spell
.skillLine
)
1121 uint32 spellCount
= 0;
1122 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
1123 spellIter
!= GetPlayer()->GetSpellMap().end();
1126 for(SkillLineAbilityMap::const_iterator skillIter
= spellmgr
.GetBeginSkillLineAbilityMap(spellIter
->first
);
1127 skillIter
!= spellmgr
.GetEndSkillLineAbilityMap(spellIter
->first
);
1130 if(skillIter
->second
->skillId
== achievementCriteria
->learn_skillline_spell
.skillLine
)
1134 SetCriteriaProgress(achievementCriteria
, spellCount
);
1137 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION
:
1138 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1140 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION
:
1141 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1143 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS
:
1144 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1146 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
1151 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
1154 // those requirements couldn't be found in the dbc
1155 AchievementCriteriaDataSet
const* data
= achievementmgr
.GetCriteriaDataSet(achievementCriteria
);
1159 if(!data
->Meets(GetPlayer(),unit
))
1162 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1165 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE
:
1167 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skill_line
.skillLine
)
1170 uint32 spellCount
= 0;
1171 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
1172 spellIter
!= GetPlayer()->GetSpellMap().end();
1175 for(SkillLineAbilityMap::const_iterator skillIter
= spellmgr
.GetBeginSkillLineAbilityMap(spellIter
->first
);
1176 skillIter
!= spellmgr
.GetEndSkillLineAbilityMap(spellIter
->first
);
1179 if(skillIter
->second
->skillId
== achievementCriteria
->learn_skill_line
.skillLine
)
1183 SetCriteriaProgress(achievementCriteria
, spellCount
);
1186 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL
:
1187 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
));
1189 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS
:
1190 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->hk_class
.classID
)
1193 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1195 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE
:
1196 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->hk_race
.raceID
)
1199 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
1201 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED
:
1202 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetMoney(), PROGRESS_HIGHEST
);
1204 // std case: not exist in DBC, not triggered in code as result
1205 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH
:
1206 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER
:
1207 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR
:
1208 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER
:
1209 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT
:
1210 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING
:
1212 // FIXME: not triggered in code as result, need to implement
1213 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG
:
1214 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY
:
1215 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID
:
1216 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE
:
1217 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA
:
1218 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA
:
1219 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA
:
1220 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL
:
1221 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING
:
1222 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING
:
1223 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK
:
1224 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
:
1225 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS
:
1226 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS
:
1227 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL
:
1228 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE
:
1229 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE
:
1230 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS
:
1231 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION
:
1232 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS
:
1233 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM
:
1234 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM
:
1235 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL
:
1236 break; // Not implemented yet :(
1238 if(IsCompletedCriteria(achievementCriteria
,achievement
))
1239 CompletedCriteriaFor(achievement
);
1241 // check again the completeness for SUMM and REQ COUNT achievements,
1242 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1243 if (achievement
->flags
& ACHIEVEMENT_FLAG_SUMM
)
1245 if (IsCompletedAchievement(achievement
))
1246 CompletedAchievement(achievement
);
1249 if(AchievementEntryList
const* achRefList
= achievementmgr
.GetAchievementByReferencedId(achievement
->ID
))
1251 for(AchievementEntryList::const_iterator itr
= achRefList
->begin(); itr
!= achRefList
->end(); ++itr
)
1252 if(IsCompletedAchievement(*itr
))
1253 CompletedAchievement(*itr
);
1258 static const uint32 achievIdByClass
[MAX_CLASSES
] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1259 static const uint32 achievIdByRace
[MAX_RACES
] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1261 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry
const* achievementCriteria
, AchievementEntry
const* achievement
)
1263 // counter can never complete
1264 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
1267 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_REACH
| ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
1269 // someone on this realm has already completed that achievement
1270 if(achievementmgr
.IsRealmCompleted(achievement
))
1274 CriteriaProgressMap::const_iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
1275 if(itr
== m_criteriaProgress
.end())
1278 CriteriaProgress
const* progress
= &itr
->second
;
1280 switch(achievementCriteria
->requiredType
)
1282 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
1283 return progress
->counter
>= achievementCriteria
->kill_creature
.creatureCount
;
1284 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
1286 // skip wrong class achievements
1287 for(int i
= 1; i
< MAX_CLASSES
; ++i
)
1288 if(achievIdByClass
[i
] == achievement
->ID
&& i
!= GetPlayer()->getClass())
1291 // skip wrong race achievements
1292 for(int i
= 1; i
< MAX_RACES
; ++i
)
1293 if(achievIdByRace
[i
] == achievement
->ID
&& i
!= GetPlayer()->getRace())
1296 // appropriate class/race or not class/race specific
1297 return progress
->counter
>= achievementCriteria
->reach_level
.level
;
1299 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
1300 return progress
->counter
>= achievementCriteria
->reach_skill_level
.skillLevel
;
1301 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
1302 return progress
->counter
>= 1;
1303 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
1304 return progress
->counter
>= achievementCriteria
->complete_quest_count
.totalQuestCount
;
1305 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
1306 return progress
->counter
>= achievementCriteria
->complete_quests_in_zone
.questCount
;
1307 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
:
1308 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
:
1309 return progress
->counter
>= achievementCriteria
->healing_done
.count
;
1310 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
1311 return progress
->counter
>= achievementCriteria
->complete_daily_quest
.questCount
;
1312 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
1313 return progress
->counter
>= achievementCriteria
->fall_without_dying
.fallHeight
;
1314 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
1315 return progress
->counter
>= 1;
1316 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
1317 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
1318 return progress
->counter
>= achievementCriteria
->be_spell_target
.spellCount
;
1319 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
1320 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
1321 return progress
->counter
>= achievementCriteria
->cast_spell
.castCount
;
1322 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
1323 return progress
->counter
>= 1;
1324 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
1325 return progress
->counter
>= achievementCriteria
->own_item
.itemCount
;
1326 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
:
1327 return progress
->counter
>= achievementCriteria
->win_rated_arena
.count
;
1328 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
:
1329 return progress
->counter
>= (achievementCriteria
->learn_skill_level
.skillLevel
* 75);
1330 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
1331 return progress
->counter
>= achievementCriteria
->use_item
.itemCount
;
1332 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
1333 return progress
->counter
>= achievementCriteria
->loot_item
.itemCount
;
1334 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
1335 return progress
->counter
>= 1;
1336 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
1337 return progress
->counter
>= achievementCriteria
->buy_bank_slot
.numberOfSlots
;
1338 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
1339 return progress
->counter
>= achievementCriteria
->gain_reputation
.reputationAmount
;
1340 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
1341 return progress
->counter
>= achievementCriteria
->gain_exalted_reputation
.numberOfExaltedFactions
;
1342 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
1343 return progress
->counter
>= achievementCriteria
->visit_barber
.numberOfVisits
;
1344 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
:
1345 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
:
1346 return progress
->counter
>= achievementCriteria
->roll_greed_on_loot
.count
;
1347 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS
:
1348 return progress
->counter
>= achievementCriteria
->hk_class
.count
;
1349 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE
:
1350 return progress
->counter
>= achievementCriteria
->hk_race
.count
;
1351 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
1352 return progress
->counter
>= achievementCriteria
->do_emote
.count
;
1353 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
:
1354 return progress
->counter
>= achievementCriteria
->equip_item
.count
;
1355 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
:
1356 return progress
->counter
>= achievementCriteria
->quest_reward_money
.goldInCopper
;
1357 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
:
1358 return progress
->counter
>= achievementCriteria
->loot_money
.goldInCopper
;
1359 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT
:
1360 return progress
->counter
>= achievementCriteria
->use_gameobject
.useCount
;
1361 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT
:
1362 return progress
->counter
>= achievementCriteria
->fish_in_gameobject
.lootCount
;
1363 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
1364 return progress
->counter
>= achievementCriteria
->learn_skillline_spell
.spellCount
;
1365 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL
:
1366 return progress
->counter
>= achievementCriteria
->win_duel
.duelCount
;
1367 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
:
1368 return progress
->counter
>= achievementCriteria
->loot_type
.lootTypeCount
;
1369 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE
:
1370 return progress
->counter
>= achievementCriteria
->learn_skill_line
.spellCount
;
1371 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL
:
1372 return progress
->counter
>= achievementCriteria
->honorable_kill
.killCount
;
1374 // handle all statistic-only criteria here
1375 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
1376 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
1377 case ACHIEVEMENT_CRITERIA_TYPE_DEATH
:
1378 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
:
1379 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
1380 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
1381 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
:
1382 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS
:
1383 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS
:
1384 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER
:
1385 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL
:
1386 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL
:
1387 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID
:
1388 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD
:
1389 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED
:
1390 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION
:
1391 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION
:
1392 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS
:
1393 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED
:
1394 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED
:
1395 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH
:
1396 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER
:
1397 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR
:
1398 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED
:
1399 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN
:
1400 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS
:
1406 void AchievementMgr::CompletedCriteriaFor(AchievementEntry
const* achievement
)
1408 // counter can never complete
1409 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
1412 // already completed and stored
1413 if (m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
1416 if (IsCompletedAchievement(achievement
))
1417 CompletedAchievement(achievement
);
1420 bool AchievementMgr::IsCompletedAchievement(AchievementEntry
const* entry
)
1422 // counter can never complete
1423 if(entry
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
1426 // for achievement with referenced achievement criterias get from referenced and counter from self
1427 uint32 achievmentForTestId
= entry
->refAchievement
? entry
->refAchievement
: entry
->ID
;
1428 uint32 achievmentForTestCount
= entry
->count
;
1430 AchievementCriteriaEntryList
const* cList
= achievementmgr
.GetAchievementCriteriaByAchievement(achievmentForTestId
);
1435 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1436 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1437 if (entry
->flags
& ACHIEVEMENT_FLAG_SUMM
)
1439 for(AchievementCriteriaEntryList::const_iterator itr
= cList
->begin(); itr
!= cList
->end(); ++itr
)
1441 AchievementCriteriaEntry
const* criteria
= *itr
;
1443 CriteriaProgressMap::const_iterator itrProgress
= m_criteriaProgress
.find(criteria
->ID
);
1444 if(itrProgress
== m_criteriaProgress
.end())
1447 CriteriaProgress
const* progress
= &itrProgress
->second
;
1448 count
+= progress
->counter
;
1450 // for counters, field4 contains the main count requirement
1451 if (count
>= criteria
->raw
.count
)
1457 // Default case - need complete all or
1458 bool completed_all
= true;
1459 for(AchievementCriteriaEntryList::const_iterator itr
= cList
->begin(); itr
!= cList
->end(); ++itr
)
1461 AchievementCriteriaEntry
const* criteria
= *itr
;
1463 bool completed
= IsCompletedCriteria(criteria
,entry
);
1465 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1469 completed_all
= false;
1471 // completed as have req. count of completed criterias
1472 if(achievmentForTestCount
> 0 && achievmentForTestCount
<= count
)
1476 // all criterias completed requirement
1477 if(completed_all
&& achievmentForTestCount
==0)
1483 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry
const* entry
, uint32 changeValue
, ProgressType ptype
)
1485 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
1486 sLog
.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry
->ID
, changeValue
, m_player
->GetGUIDLow());
1488 CriteriaProgress
*progress
= NULL
;
1490 CriteriaProgressMap::iterator iter
= m_criteriaProgress
.find(entry
->ID
);
1492 if(iter
== m_criteriaProgress
.end())
1494 // not create record for 0 counter
1495 if(changeValue
== 0)
1498 progress
= &m_criteriaProgress
[entry
->ID
];
1499 progress
->counter
= changeValue
;
1500 progress
->date
= time(NULL
);
1504 progress
= &iter
->second
;
1506 uint32 newValue
= 0;
1510 newValue
= changeValue
;
1512 case PROGRESS_ACCUMULATE
:
1515 uint32 max_value
= std::numeric_limits
<uint32
>::max();
1516 newValue
= max_value
- progress
->counter
> changeValue
? progress
->counter
+ changeValue
: max_value
;
1519 case PROGRESS_HIGHEST
:
1520 newValue
= progress
->counter
< changeValue
? changeValue
: progress
->counter
;
1524 // not update (not mark as changed) if counter will have same value
1525 if(progress
->counter
== newValue
)
1528 progress
->counter
= newValue
;
1531 progress
->changed
= true;
1533 if(entry
->timeLimit
)
1535 time_t now
= time(NULL
);
1536 if(time_t(progress
->date
+ entry
->timeLimit
) < now
)
1537 progress
->counter
= 1;
1539 // also it seems illogical, the timeframe will be extended at every criteria update
1540 progress
->date
= now
;
1542 SendCriteriaUpdate(entry
->ID
,progress
);
1545 void AchievementMgr::CompletedAchievement(AchievementEntry
const* achievement
)
1547 sLog
.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement
->ID
);
1548 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
|| m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
1551 SendAchievementEarned(achievement
);
1552 CompletedAchievementData
& ca
= m_completedAchievements
[achievement
->ID
];
1553 ca
.date
= time(NULL
);
1556 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1557 // TODO: where do set this instead?
1558 if(!(achievement
->flags
& ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
1559 achievementmgr
.SetRealmCompleted(achievement
);
1561 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
);
1563 // reward items and titles if any
1564 AchievementReward
const* reward
= achievementmgr
.GetAchievementReward(achievement
);
1571 if(uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
? 0 : 1])
1573 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
1574 GetPlayer()->SetTitle(titleEntry
);
1580 Item
* item
= reward
->itemId
? Item::CreateItem(reward
->itemId
,1,GetPlayer ()) : NULL
;
1585 // save new item before send
1586 item
->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1589 mi
.AddItem(item
->GetGUIDLow(), item
->GetEntry(), item
);
1592 int loc_idx
= GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1595 std::string subject
= reward
->subject
;
1596 std::string text
= reward
->text
;
1599 if(AchievementRewardLocale
const* loc
= achievementmgr
.GetAchievementRewardLocale(achievement
))
1601 if (loc
->subject
.size() > size_t(loc_idx
) && !loc
->subject
[loc_idx
].empty())
1602 subject
= loc
->subject
[loc_idx
];
1603 if (loc
->text
.size() > size_t(loc_idx
) && !loc
->text
[loc_idx
].empty())
1604 text
= loc
->text
[loc_idx
];
1608 uint32 itemTextId
= objmgr
.CreateItemText( text
);
1610 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE
, MAIL_STATIONERY_NORMAL
, reward
->sender
, GetPlayer()->GetGUIDLow(), subject
, itemTextId
, &mi
, 0, 0, MAIL_CHECK_MASK_NONE
);
1614 void AchievementMgr::SendAllAchievementData()
1616 // since we don't know the exact size of the packed GUIDs this is just an approximation
1617 WorldPacket
data(SMSG_ALL_ACHIEVEMENT_DATA
, 4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
1618 BuildAllDataPacket(&data
);
1619 GetPlayer()->GetSession()->SendPacket(&data
);
1622 void AchievementMgr::SendRespondInspectAchievements(Player
* player
)
1624 // since we don't know the exact size of the packed GUIDs this is just an approximation
1625 WorldPacket
data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS
, 4+4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
1626 data
.append(GetPlayer()->GetPackGUID());
1627 BuildAllDataPacket(&data
);
1628 player
->GetSession()->SendPacket(&data
);
1632 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1634 void AchievementMgr::BuildAllDataPacket(WorldPacket
*data
)
1636 for(CompletedAchievementMap::const_iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
1638 *data
<< uint32(iter
->first
);
1639 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
1643 for(CriteriaProgressMap::const_iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
1645 *data
<< uint32(iter
->first
);
1646 data
->appendPackGUID(iter
->second
.counter
);
1647 data
->append(GetPlayer()->GetPackGUID());
1649 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
1657 //==========================================================
1658 AchievementCriteriaEntryList
const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type
)
1660 return m_AchievementCriteriasByType
[type
];
1663 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1665 if(sAchievementCriteriaStore
.GetNumRows()==0)
1671 sLog
.outErrorDb(">> Loaded 0 achievement criteria.");
1675 barGoLink
bar( sAchievementCriteriaStore
.GetNumRows() );
1676 for (uint32 entryId
= 0; entryId
< sAchievementCriteriaStore
.GetNumRows(); ++entryId
)
1680 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
1684 m_AchievementCriteriasByType
[criteria
->requiredType
].push_back(criteria
);
1685 m_AchievementCriteriaListByAchievement
[criteria
->referredAchievement
].push_back(criteria
);
1689 sLog
.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType
->size());
1692 void AchievementGlobalMgr::LoadAchievementReferenceList()
1694 if(sAchievementStore
.GetNumRows()==0)
1700 sLog
.outErrorDb(">> Loaded 0 achievement references.");
1705 barGoLink
bar( sAchievementStore
.GetNumRows() );
1706 for (uint32 entryId
= 0; entryId
< sAchievementStore
.GetNumRows(); ++entryId
)
1710 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(entryId
);
1711 if(!achievement
|| !achievement
->refAchievement
)
1714 m_AchievementListByReferencedId
[achievement
->refAchievement
].push_back(achievement
);
1719 sLog
.outString(">> Loaded %u achievement references.",count
);
1722 void AchievementGlobalMgr::LoadAchievementCriteriaData()
1724 m_criteriaDataMap
.clear(); // need for reload case
1726 QueryResult
*result
= WorldDatabase
.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
1734 sLog
.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
1739 barGoLink
bar(result
->GetRowCount());
1743 Field
*fields
= result
->Fetch();
1744 uint32 criteria_id
= fields
[0].GetUInt32();
1746 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(criteria_id
);
1750 sLog
.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id
);
1754 AchievementCriteriaData
data(fields
[1].GetUInt32(),fields
[2].GetUInt32(),fields
[3].GetUInt32());
1756 if(!data
.IsValid(criteria
))
1759 // this will allocate empty data set storage
1760 AchievementCriteriaDataSet
& dataSet
= m_criteriaDataMap
[criteria_id
];
1762 // add real data only for not NONE data types
1763 if(data
.dataType
!=ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE
)
1766 // counting data by and data types
1768 } while(result
->NextRow());
1772 // post loading checks
1773 for (uint32 entryId
= 0; entryId
< sAchievementCriteriaStore
.GetNumRows(); ++entryId
)
1775 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
1779 switch(criteria
->requiredType
)
1781 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
: // need skip generic cases
1782 if(criteria
->win_rated_arena
.flag
!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE
)
1785 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
: // need skip generic cases
1786 if(criteria
->do_emote
.count
==0)
1789 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
: // any cases
1791 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
: // need skip generic cases
1792 if(criteria
->loot_type
.lootTypeCount
!=1)
1795 default: // type not use DB data, ignore
1799 if(!GetCriteriaDataSet(criteria
))
1800 sLog
.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria
->ID
, criteria
->requiredType
);
1804 sLog
.outString(">> Loaded %u additional achievement criteria data.",count
);
1807 void AchievementGlobalMgr::LoadCompletedAchievements()
1809 QueryResult
*result
= CharacterDatabase
.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1817 sLog
.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1821 barGoLink
bar(result
->GetRowCount());
1825 Field
*fields
= result
->Fetch();
1827 uint32 achievement_id
= fields
[0].GetUInt32();
1828 if(!sAchievementStore
.LookupEntry(achievement_id
))
1830 // we will remove not existed achievement for all characters
1831 sLog
.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id
);
1832 CharacterDatabase
.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id
);
1836 m_allCompletedAchievements
.insert(achievement_id
);
1837 } while(result
->NextRow());
1842 sLog
.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements
.size());
1845 void AchievementGlobalMgr::LoadRewards()
1847 m_achievementRewards
.clear(); // need for reload case
1850 QueryResult
*result
= WorldDatabase
.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1859 sLog
.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1864 barGoLink
bar(result
->GetRowCount());
1870 Field
*fields
= result
->Fetch();
1871 uint32 entry
= fields
[0].GetUInt32();
1872 if (!sAchievementStore
.LookupEntry(entry
))
1874 sLog
.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry
);
1878 AchievementReward reward
;
1879 reward
.titleId
[0] = fields
[1].GetUInt32();
1880 reward
.titleId
[1] = fields
[2].GetUInt32();
1881 reward
.itemId
= fields
[3].GetUInt32();
1882 reward
.sender
= fields
[4].GetUInt32();
1883 reward
.subject
= fields
[5].GetCppString();
1884 reward
.text
= fields
[6].GetCppString();
1886 if ((reward
.titleId
[0]==0)!=(reward
.titleId
[1]==0))
1887 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]);
1889 // must be title or mail at least
1890 if (!reward
.titleId
[0] && !reward
.titleId
[1] && !reward
.sender
)
1892 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry
);
1896 if (reward
.titleId
[0])
1898 CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(reward
.titleId
[0]);
1901 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry
, reward
.titleId
[0]);
1902 reward
.titleId
[0] = 0;
1906 if (reward
.titleId
[1])
1908 CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(reward
.titleId
[1]);
1911 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry
, reward
.titleId
[1]);
1912 reward
.titleId
[1] = 0;
1916 //check mail data before item for report including wrong item case
1919 if (!objmgr
.GetCreatureTemplate(reward
.sender
))
1921 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry
, reward
.sender
);
1928 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry
);
1930 if (!reward
.subject
.empty())
1931 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry
);
1933 if (!reward
.text
.empty())
1934 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry
);
1939 if (!objmgr
.GetItemPrototype(reward
.itemId
))
1941 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry
, reward
.itemId
);
1946 m_achievementRewards
[entry
] = reward
;
1949 } while (result
->NextRow());
1954 sLog
.outString( ">> Loaded %u achievement rewards", count
);
1957 void AchievementGlobalMgr::LoadRewardLocales()
1959 m_achievementRewardLocales
.clear(); // need for reload case
1961 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");
1970 sLog
.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1974 barGoLink
bar(result
->GetRowCount());
1978 Field
*fields
= result
->Fetch();
1981 uint32 entry
= fields
[0].GetUInt32();
1983 if(m_achievementRewards
.find(entry
)==m_achievementRewards
.end())
1985 sLog
.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry
);
1989 AchievementRewardLocale
& data
= m_achievementRewardLocales
[entry
];
1991 for(int i
= 1; i
< MAX_LOCALE
; ++i
)
1993 std::string str
= fields
[1+2*(i
-1)].GetCppString();
1996 int idx
= objmgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
1999 if(data
.subject
.size() <= size_t(idx
))
2000 data
.subject
.resize(idx
+1);
2002 data
.subject
[idx
] = str
;
2005 str
= fields
[1+2*(i
-1)+1].GetCppString();
2008 int idx
= objmgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
2011 if(data
.text
.size() <= size_t(idx
))
2012 data
.text
.resize(idx
+1);
2014 data
.text
[idx
] = str
;
2018 } while (result
->NextRow());
2023 sLog
.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales
.size() );