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
);
40 const CriteriaCastSpellRequirement
AchievementGlobalMgr::m_criteriaCastSpellRequirements
[CRITERIA_CAST_SPELL_REQ_COUNT
] =
46 {5772, 0, 0, RACE_GNOME
},
47 {5774, 0, 0, RACE_BLOODELF
},
48 {5775, 0, 0, RACE_DRAENEI
},
49 {5776, 0, 0, RACE_DWARF
},
50 {5777, 0, 0, RACE_HUMAN
},
51 {5778, 0, 0, RACE_NIGHTELF
},
52 {5779, 0, 0, RACE_ORC
},
53 {5780, 0, 0, RACE_TAUREN
},
54 {5781, 0, 0, RACE_TROLL
},
55 {5782, 0, 0, RACE_UNDEAD_PLAYER
},
67 {6237, 0, CLASS_DEATH_KNIGHT
, RACE_ORC
},
68 {6238, 0, CLASS_WARRIOR
, RACE_HUMAN
},
69 {6239, 0, CLASS_SHAMAN
, RACE_TAUREN
},
70 {6240, 0, CLASS_DRUID
, RACE_NIGHTELF
},
71 {6241, 0, CLASS_ROGUE
, RACE_UNDEAD_PLAYER
},
72 {6242, 0, CLASS_HUNTER
, RACE_TROLL
},
73 {6243, 0, CLASS_MAGE
, RACE_GNOME
},
74 {6244, 0, CLASS_PALADIN
, RACE_DWARF
},
75 {6245, 0, CLASS_WARLOCK
, RACE_BLOODELF
},
76 {6246, 0, CLASS_PRIEST
, RACE_DRAENEI
},
77 {6312, 0, CLASS_WARLOCK
, RACE_GNOME
},
78 {6313, 0, CLASS_DEATH_KNIGHT
, RACE_HUMAN
},
79 {6314, 0, CLASS_PRIEST
, RACE_NIGHTELF
},
80 {6315, 0, CLASS_SHAMAN
, RACE_ORC
},
81 {6316, 0, CLASS_DRUID
, RACE_TAUREN
},
82 {6317, 0, CLASS_ROGUE
, RACE_TROLL
},
83 {6318, 0, CLASS_WARRIOR
, RACE_UNDEAD_PLAYER
},
84 {6319, 0, CLASS_MAGE
, RACE_BLOODELF
},
85 {6320, 0, CLASS_PALADIN
, RACE_DRAENEI
},
86 {6321, 0, CLASS_HUNTER
, RACE_DWARF
},
92 class AchievementChatBuilder
95 AchievementChatBuilder(Player
const& pl
, ChatMsg msgtype
, int32 textId
, uint32 ach_id
)
96 : i_player(pl
), i_msgtype(msgtype
), i_textId(textId
), i_achievementId(ach_id
) {}
97 void operator()(WorldPacket
& data
, int32 loc_idx
)
99 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
101 data
<< uint8(i_msgtype
);
102 data
<< uint32(LANG_UNIVERSAL
);
103 data
<< uint64(i_player
.GetGUID());
105 data
<< uint64(i_player
.GetGUID());
106 data
<< uint32(strlen(text
)+1);
109 data
<< uint32(i_achievementId
);
113 Player
const& i_player
;
116 uint32 i_achievementId
;
118 } // namespace MaNGOS
120 AchievementMgr::AchievementMgr(Player
*player
)
125 AchievementMgr::~AchievementMgr()
129 void AchievementMgr::Reset()
131 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
133 WorldPacket
data(SMSG_ACHIEVEMENT_DELETED
,4);
134 data
<< uint32(iter
->first
);
135 m_player
->SendDirectMessage(&data
);
138 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
140 WorldPacket
data(SMSG_CRITERIA_DELETED
,4);
141 data
<< uint32(iter
->first
);
142 m_player
->SendDirectMessage(&data
);
145 m_completedAchievements
.clear();
146 m_criteriaProgress
.clear();
147 DeleteFromDB(m_player
->GetGUIDLow());
150 CheckAllAchievementCriteria();
153 void AchievementMgr::DeleteFromDB(uint32 lowguid
)
155 CharacterDatabase
.BeginTransaction ();
156 CharacterDatabase
.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid
);
157 CharacterDatabase
.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid
);
158 CharacterDatabase
.CommitTransaction ();
161 void AchievementMgr::SaveToDB()
163 if(!m_completedAchievements
.empty())
165 bool need_execute
= false;
166 std::ostringstream ssdel
;
167 std::ostringstream ssins
;
168 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
170 if(!iter
->second
.changed
)
173 /// first new/changed record prefix
176 ssdel
<< "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
177 ssins
<< "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
180 /// next new/changed record prefix
187 // new/changed record data
188 ssdel
<< iter
->first
;
189 ssins
<< "("<<GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << uint64(iter
->second
.date
) << ")";
191 /// mark as saved in db
192 iter
->second
.changed
= false;
200 CharacterDatabase
.Execute( ssdel
.str().c_str() );
201 CharacterDatabase
.Execute( ssins
.str().c_str() );
205 if(!m_criteriaProgress
.empty())
207 /// prepare deleting and insert
208 bool need_execute_del
= false;
209 bool need_execute_ins
= false;
210 std::ostringstream ssdel
;
211 std::ostringstream ssins
;
212 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
214 if(!iter
->second
.changed
)
217 // deleted data (including 0 progress state)
219 /// first new/changed record prefix (for any counter value)
220 if(!need_execute_del
)
222 ssdel
<< "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
223 need_execute_del
= true;
225 /// next new/changed record prefix
229 // new/changed record data
230 ssdel
<< iter
->first
;
233 // store data only for real progress
234 if(iter
->second
.counter
!= 0)
236 /// first new/changed record prefix
237 if(!need_execute_ins
)
239 ssins
<< "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
240 need_execute_ins
= true;
242 /// next new/changed record prefix
246 // new/changed record data
247 ssins
<< "(" << GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << iter
->second
.counter
<< ", " << iter
->second
.date
<< ")";
250 /// mark as updated in db
251 iter
->second
.changed
= false;
254 if(need_execute_del
) // DELETE ... IN (.... _)_
257 if(need_execute_del
|| need_execute_ins
)
260 CharacterDatabase
.Execute( ssdel
.str().c_str() );
262 CharacterDatabase
.Execute( ssins
.str().c_str() );
267 void AchievementMgr::LoadFromDB(QueryResult
*achievementResult
, QueryResult
*criteriaResult
)
269 if(achievementResult
)
273 Field
*fields
= achievementResult
->Fetch();
274 CompletedAchievementData
& ca
= m_completedAchievements
[fields
[0].GetUInt32()];
275 ca
.date
= time_t(fields
[1].GetUInt64());
277 } while(achievementResult
->NextRow());
278 delete achievementResult
;
285 Field
*fields
= criteriaResult
->Fetch();
287 uint32 id
= fields
[0].GetUInt32();
288 uint32 counter
= fields
[1].GetUInt32();
289 time_t date
= time_t(fields
[2].GetUInt64());
291 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(id
);
292 if (!criteria
|| (criteria
->timeLimit
&& time_t(date
+ criteria
->timeLimit
) < time(NULL
)))
295 CriteriaProgress
& progress
= m_criteriaProgress
[id
];
296 progress
.counter
= counter
;
297 progress
.date
= date
;
298 progress
.changed
= false;
299 } while(criteriaResult
->NextRow());
300 delete criteriaResult
;
305 void AchievementMgr::SendAchievementEarned(AchievementEntry
const* achievement
)
307 if(GetPlayer()->GetSession()->PlayerLoading())
311 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
312 sLog
.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement
->ID
);
315 if(Guild
* guild
= objmgr
.GetGuildById(GetPlayer()->GetGuildId()))
317 MaNGOS::AchievementChatBuilder
say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT
, LANG_ACHIEVEMENT_EARNED
,achievement
->ID
);
318 MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> say_do(say_builder
);
319 guild
->BroadcastWorker(say_do
,GetPlayer());
322 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_KILL
|ACHIEVEMENT_FLAG_REALM_FIRST_REACH
))
324 // broadcast realm first reached
325 WorldPacket
data(SMSG_SERVER_FIRST_ACHIEVEMENT
, strlen(GetPlayer()->GetName())+1+8+4+4);
326 data
<< GetPlayer()->GetName();
327 data
<< uint64(GetPlayer()->GetGUID());
328 data
<< uint32(achievement
->ID
);
329 data
<< uint32(0); // 1=link supplied string as player name, 0=display plain string
330 sWorld
.SendGlobalMessage(&data
);
334 CellPair p
= MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
337 cell
.data
.Part
.reserved
= ALL_DISTRICT
;
340 MaNGOS::AchievementChatBuilder
say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT
, LANG_ACHIEVEMENT_EARNED
,achievement
->ID
);
341 MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> say_do(say_builder
);
342 MaNGOS::PlayerDistWorker
<MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> > say_worker(GetPlayer(),sWorld
.getConfig(CONFIG_LISTEN_RANGE_SAY
),say_do
);
343 TypeContainerVisitor
<MaNGOS::PlayerDistWorker
<MaNGOS::LocalizedPacketDo
<MaNGOS::AchievementChatBuilder
> >, WorldTypeMapContainer
> message(say_worker
);
344 CellLock
<GridReadGuard
> cell_lock(cell
, p
);
345 cell_lock
->Visit(cell_lock
, message
, *GetPlayer()->GetMap());
348 WorldPacket
data(SMSG_ACHIEVEMENT_EARNED
, 8+4+8);
349 data
.append(GetPlayer()->GetPackGUID());
350 data
<< uint32(achievement
->ID
);
351 data
<< uint32(secsToTimeBitFields(time(NULL
)));
353 GetPlayer()->SendMessageToSetInRange(&data
, sWorld
.getConfig(CONFIG_LISTEN_RANGE_SAY
), true);
356 void AchievementMgr::SendCriteriaUpdate(uint32 id
, CriteriaProgress
const* progress
)
358 WorldPacket
data(SMSG_CRITERIA_UPDATE
, 8+4+8);
361 // the counter is packed like a packed Guid
362 data
.appendPackGUID(progress
->counter
);
364 data
.append(GetPlayer()->GetPackGUID());
366 data
<< uint32(secsToTimeBitFields(progress
->date
));
367 data
<< uint32(0); // timer 1
368 data
<< uint32(0); // timer 2
369 GetPlayer()->SendDirectMessage(&data
);
373 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
375 void AchievementMgr::CheckAllAchievementCriteria()
377 // suppress sending packets
378 for(uint32 i
=0; i
<ACHIEVEMENT_CRITERIA_TYPE_TOTAL
; ++i
)
379 UpdateAchievementCriteria(AchievementCriteriaTypes(i
));
382 static const uint32 achievIdByArenaSlot
[MAX_ARENA_SLOT
] = { 1057, 1107, 1108 };
383 static const uint32 achievIdForDangeon
[][4] =
385 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
386 { 321, true, true, true },
387 { 916, false, true, false },
388 { 917, false, true, false },
389 { 918, true, false, false },
390 { 2219, false, false, true },
391 { 0, false, false, false }
395 * this function will be called whenever the user might have done a criteria relevant action
397 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
, uint32 miscvalue2
, Unit
*unit
, uint32 time
)
399 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
400 sLog
.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type
, miscvalue1
, miscvalue2
, time
);
402 if (!sWorld
.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS
) && m_player
->GetSession()->GetSecurity() > SEC_PLAYER
)
405 AchievementCriteriaEntryList
const& achievementCriteriaList
= achievementmgr
.GetAchievementCriteriaByType(type
);
406 for(AchievementCriteriaEntryList::const_iterator i
= achievementCriteriaList
.begin(); i
!=achievementCriteriaList
.end(); ++i
)
408 AchievementCriteriaEntry
const *achievementCriteria
= (*i
);
410 if (achievementCriteria
->groupFlag
& ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP
&& GetPlayer()->GetGroup())
413 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
417 if ((achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_HORDE
&& GetPlayer()->GetTeam() != HORDE
) ||
418 (achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_ALLIANCE
&& GetPlayer()->GetTeam() != ALLIANCE
))
421 // don't update already completed criteria
422 if (IsCompletedCriteria(achievementCriteria
,achievement
))
427 // std. case: increment at 1
428 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
429 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
432 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
434 // std case: increment at miscvalue1
435 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS
:
436 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
:
437 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING
:
438 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER
:
439 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL
:
440 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
:
441 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
444 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
446 // std case: high value at miscvalue1
447 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID
:
448 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD
: /* FIXME: for online player only currently */
449 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
452 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_HIGHEST
);
457 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
458 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
461 if(achievementCriteria
->kill_creature
.creatureID
!= miscvalue1
)
464 // LOT achievement->ID required special custom checks
465 switch(achievement
->ID
)
468 case 489: case 490: case 491: case 492: case 493: case 494: case 495:
469 case 496: case 497: case 498: case 499: case 500: case 563: case 565:
470 case 567: case 569: case 573: case 575: case 577: case 623: case 625:
471 case 667: case 668: case 669: case 670: case 671: case 672: case 673:
472 case 674: case 675: case 676: case 677: case 678: case 679: case 680:
473 case 681: case 682: case 1367: case 1368: case 1378: case 1379:
474 case 1380: case 1381: case 1382: case 1383: case 1384: case 1385:
475 case 1386: case 1387: case 1388: case 1389: case 1390: case 1393:
476 case 1394: case 1400: case 1402: case 1504: case 1505: case 1506:
477 case 1507: case 1508: case 1509: case 1510: case 1511: case 1512:
478 case 1513: case 1514: case 1515: case 1721: case 1754: case 1756:
479 case 1768: case 1817: case 1865:
480 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC
)
484 case 579: case 1296: case 1297: case 1816: case 1834: case 1857: case 1859:
485 case 1860: case 1861: case 1862: case 1864: case 1866: case 1867: case 1868:
486 case 1870: case 1871: case 1872: case 1873: case 1875: case 1877: case 1919:
487 case 2036: case 2037: case 2038: case 2039: case 2040: case 2041: case 2042:
488 case 2043: case 2044: case 2045: case 2046: case 2048: case 2052: case 2053:
489 case 2054: case 2056: case 2057: case 2058: case 2139: case 2140: case 2147:
490 case 2149: case 2150: case 2151: case 2152: case 2154: case 2155: case 2156:
491 case 2157: case 2179: case 2181: case 2183: case 2185: case 2186:
492 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC
)
494 // FIX ME: mark as fail always until implement
497 case 578: case 624: case 1790: case 1856: case 1858: case 1869: case 1874:
498 case 1996: case 1997: case 2047: case 2049: case 2050: case 2051: case 2146:
499 case 2148: case 2153: case 2178: case 2180: case 2182: case 2184: case 2187:
500 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL
)
502 // FIX ME: mark as fail always until implement
506 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL
)
511 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
513 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
514 SetCriteriaProgress(achievementCriteria
, GetPlayer()->getLevel());
516 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
517 // update at loading or specific skill update
518 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->reach_skill_level
.skillID
)
520 if(uint32 skillvalue
= GetPlayer()->GetBaseSkillValue(achievementCriteria
->reach_skill_level
.skillID
))
521 SetCriteriaProgress(achievementCriteria
, skillvalue
);
523 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
:
524 // update at loading or specific skill update
525 if(miscvalue1
&& miscvalue1
!= achievementCriteria
->learn_skill_level
.skillID
)
527 if(uint32 maxSkillvalue
= GetPlayer()->GetPureMaxSkillValue(achievementCriteria
->learn_skill_level
.skillID
))
528 SetCriteriaProgress(achievementCriteria
, maxSkillvalue
);
530 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
531 if(m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end())
532 SetCriteriaProgress(achievementCriteria
, 1);
534 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
537 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
538 if(itr
->second
.m_rewarded
)
540 SetCriteriaProgress(achievementCriteria
, counter
);
543 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
546 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
548 Quest
const* quest
= objmgr
.GetQuestTemplate(itr
->first
);
549 if(itr
->second
.m_rewarded
&& quest
->GetZoneOrSort() >= 0 && uint32(quest
->GetZoneOrSort()) == achievementCriteria
->complete_quests_in_zone
.zoneID
)
552 SetCriteriaProgress(achievementCriteria
, counter
);
555 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
556 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
559 if(GetPlayer()->GetMapId() != achievementCriteria
->complete_battleground
.mapID
)
561 SetCriteriaProgress(achievementCriteria
, miscvalue1
, PROGRESS_ACCUMULATE
);
563 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
564 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
567 if(GetPlayer()->GetMapId() != achievementCriteria
->death_at_map
.mapID
)
569 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
571 case ACHIEVEMENT_CRITERIA_TYPE_DEATH
:
573 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
576 // skip wrong arena achievements, if not achievIdByArenaSlot then normal totla death counter
578 for(int i
= 0; i
< MAX_ARENA_SLOT
; ++i
)
580 if(achievIdByArenaSlot
[i
] == achievement
->ID
)
582 BattleGround
* bg
= GetPlayer()->GetBattleGround();
583 if(!bg
|| ArenaTeam::GetSlotByType(bg
->GetArenaType())!=i
)
592 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
595 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
:
597 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
601 Map
const* map
= GetPlayer()->GetMap();
602 if(!map
->IsDungeon())
607 for(int i
= 0; achievIdForDangeon
[i
][0]; ++i
)
609 if(achievIdForDangeon
[i
][0] == achievement
->ID
)
613 // if raid accepted (ignore difficulty)
614 if(!achievIdForDangeon
[i
][2])
617 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL
)
619 // dungeon in normal mode accepted
620 if(!achievIdForDangeon
[i
][1])
625 // dungeon in heroic mode accepted
626 if(!achievIdForDangeon
[i
][3])
637 //FIXME: work only for instances where max==min for players
638 if(((InstanceMap
*)map
)->GetMaxPlayers() != achievementCriteria
->death_in_dungeon
.manLimit
)
640 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
644 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
645 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
648 if(miscvalue1
!= achievementCriteria
->killed_by_creature
.creatureEntry
)
650 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
652 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
653 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
657 // if team check required: must kill by opposition faction
658 if(achievement
->ID
==318 && miscvalue2
==GetPlayer()->GetTeam())
661 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
663 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
665 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
668 if(achievement
->ID
== 1260)
670 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED
)
672 if(!IsHolidayActive(HOLIDAY_BREWFEST
))
675 // miscvalue1 is the ingame fallheight*100 as stored in dbc
676 SetCriteriaProgress(achievementCriteria
, miscvalue1
);
679 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
:
680 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
683 if(miscvalue2
!= achievementCriteria
->death_from
.type
)
685 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
687 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
688 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria
->complete_quest
.questID
))
689 SetCriteriaProgress(achievementCriteria
, 1);
691 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
692 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
693 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->be_spell_target
.spellID
)
695 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
697 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
698 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
700 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
702 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
703 // spell always provide and at login spell learning.
704 if(miscvalue1
&& miscvalue1
!=achievementCriteria
->learn_spell
.spellID
)
706 if(GetPlayer()->HasSpell(miscvalue1
))
707 SetCriteriaProgress(achievementCriteria
, 1);
709 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
710 // speedup for non-login case
711 if(miscvalue1
&& achievementCriteria
->own_item
.itemID
!= miscvalue1
)
713 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetItemCount(achievementCriteria
->own_item
.itemID
, true));
715 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
716 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
719 if(achievementCriteria
->use_item
.itemID
!= miscvalue1
)
721 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
723 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
724 // You _have_ to loot that item, just owning it when logging in does _not_ count!
727 if(miscvalue1
!= achievementCriteria
->own_item
.itemID
)
729 SetCriteriaProgress(achievementCriteria
, miscvalue2
, PROGRESS_ACCUMULATE
);
731 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
733 WorldMapOverlayEntry
const* worldOverlayEntry
= sWorldMapOverlayStore
.LookupEntry(achievementCriteria
->explore_area
.areaReference
);
734 if(!worldOverlayEntry
)
737 bool matchFound
= false;
738 for (int i
= 0; i
< 3; ++i
)
740 int32 exploreFlag
= GetAreaFlagByAreaID(worldOverlayEntry
->areatableID
[i
]);
744 uint32 playerIndexOffset
= uint32(exploreFlag
) / 32;
745 uint32 mask
= 1<< (uint32(exploreFlag
) % 32);
747 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ playerIndexOffset
) & mask
)
755 SetCriteriaProgress(achievementCriteria
, 1);
758 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
759 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetByteValue(PLAYER_BYTES_2
, 2)+1);
761 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
763 // skip faction check only at loading
764 if (miscvalue1
&& miscvalue1
!= achievementCriteria
->gain_reputation
.factionID
)
767 int32 reputation
= GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria
->gain_reputation
.factionID
);
769 SetCriteriaProgress(achievementCriteria
, reputation
);
772 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
774 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
777 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
779 // skip for login case
782 SetCriteriaProgress(achievementCriteria
, 1);
785 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
:
786 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
:
788 // miscvalue1 = itemid
789 // miscvalue2 = diced value
792 if(miscvalue2
!= achievementCriteria
->roll_greed_on_loot
.rollValue
)
794 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( miscvalue1
);
796 uint32 requiredItemLevel
= 0;
797 if (achievementCriteria
->ID
== 2412 || achievementCriteria
->ID
== 2358)
798 requiredItemLevel
= 185;
800 if(!pProto
|| pProto
->ItemLevel
<requiredItemLevel
)
802 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
805 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
807 // miscvalue1 = emote
808 // miscvalue2 = achievement->ID for special requirement
811 if(miscvalue1
!= achievementCriteria
->do_emote
.emoteID
)
813 if(achievementCriteria
->do_emote
.count
)
816 if(achievement
->ID
==247)
818 if (!unit
|| unit
->GetTypeId() != TYPEID_PLAYER
||
819 unit
->isAlive() || ((Player
*)unit
)->GetDeathTimer() == 0)
822 // expected as scripted case
823 else if(!miscvalue2
|| !achievement
->ID
!= miscvalue2
)
827 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
830 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
:
831 // miscvalue1 = item_id
834 if(miscvalue1
!= achievementCriteria
->equip_item
.itemID
)
837 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
839 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT
:
840 // miscvalue1 = go entry
843 if(miscvalue1
!= achievementCriteria
->use_gameobject
.goEntry
)
846 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
848 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
850 // spell always provide and at login spell learning.
853 // rescan only when change possible
854 SkillLineAbilityMap::const_iterator skillIter0
= spellmgr
.GetBeginSkillLineAbilityMap(miscvalue1
);
855 if(skillIter0
== spellmgr
.GetEndSkillLineAbilityMap(miscvalue1
))
857 if(skillIter0
->second
->skillId
!= achievementCriteria
->learn_skilline_spell
.skillLine
)
860 uint32 spellCount
= 0;
861 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
862 spellIter
!= GetPlayer()->GetSpellMap().end();
865 for(SkillLineAbilityMap::const_iterator skillIter
= spellmgr
.GetBeginSkillLineAbilityMap(spellIter
->first
);
866 skillIter
!= spellmgr
.GetEndSkillLineAbilityMap(spellIter
->first
);
869 if(skillIter
->second
->skillId
== achievementCriteria
->learn_skilline_spell
.skillLine
)
873 SetCriteriaProgress(achievementCriteria
, spellCount
);
876 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION
:
877 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
879 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION
:
880 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
882 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS
:
883 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
885 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
887 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
890 // those requirements couldn't be found in the dbc
891 if (CriteriaCastSpellRequirement
const* requirement
= AchievementGlobalMgr::GetCriteriaCastSpellRequirement(achievementCriteria
))
896 if (requirement
->creatureEntry
&& unit
->GetEntry() != requirement
->creatureEntry
)
899 if (requirement
->playerRace
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getRace()!=requirement
->playerRace
))
902 if (requirement
->playerClass
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getClass()!=requirement
->playerClass
))
906 SetCriteriaProgress(achievementCriteria
, 1, PROGRESS_ACCUMULATE
);
909 // std case: not exist in DBC, not triggered in code as result
910 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH
:
911 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER
:
912 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR
:
913 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER
:
914 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT
:
915 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING
:
917 // FIXME: not triggered in code as result, need to implement
918 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG
:
919 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY
:
920 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
:
921 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID
:
922 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE
:
923 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA
:
924 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA
:
925 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA
:
926 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL
:
927 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
:
928 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING
:
929 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING
:
930 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK
:
931 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
:
932 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS
:
933 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE
:
934 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
:
935 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS
:
936 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS
:
937 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS
:
938 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL
:
939 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT
:
940 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE
:
941 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL
:
942 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE
:
943 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS
:
944 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION
:
945 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS
:
946 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED
:
947 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM
:
948 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM
:
949 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED
:
950 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED
:
951 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT
:
952 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED
:
953 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED
:
954 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED
:
955 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED
:
956 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED
:
957 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED
:
958 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN
:
959 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
:
960 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE
:
961 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL
:
962 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS
:
963 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL
:
964 break; // Not implemented yet :(
966 if(IsCompletedCriteria(achievementCriteria
,achievement
))
967 CompletedCriteria(achievementCriteria
,achievement
);
970 if(AchievementEntryList
const* achRefList
= achievementmgr
.GetAchievementByReferencedId(achievement
->ID
))
972 for(AchievementEntryList::const_iterator itr
= achRefList
->begin(); itr
!= achRefList
->end(); ++itr
)
973 if(IsCompletedAchievement(*itr
))
974 CompletedAchievement(*itr
);
979 static const uint32 achievIdByClass
[MAX_CLASSES
] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
980 static const uint32 achievIdByRace
[MAX_RACES
] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
982 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry
const* achievementCriteria
, AchievementEntry
const* achievement
)
984 // counter can never complete
985 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
988 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_REACH
| ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
990 // someone on this realm has already completed that achievement
991 if(achievementmgr
.IsRealmCompleted(achievement
))
995 CriteriaProgressMap::const_iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
996 if(itr
== m_criteriaProgress
.end())
999 CriteriaProgress
const* progress
= &itr
->second
;
1001 switch(achievementCriteria
->requiredType
)
1003 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
1004 return progress
->counter
>= achievementCriteria
->kill_creature
.creatureCount
;
1005 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
1007 // skip wrong class achievements
1008 for(int i
= 1; i
< MAX_CLASSES
; ++i
)
1009 if(achievIdByClass
[i
] == achievement
->ID
&& i
!= GetPlayer()->getClass())
1012 // skip wrong race achievements
1013 for(int i
= 1; i
< MAX_RACES
; ++i
)
1014 if(achievIdByRace
[i
] == achievement
->ID
&& i
!= GetPlayer()->getRace())
1017 // appropriate class/race or not class/race specific
1018 return progress
->counter
>= achievementCriteria
->reach_level
.level
;
1020 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
1021 return progress
->counter
>= achievementCriteria
->reach_skill_level
.skillLevel
;
1022 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
:
1023 return progress
->counter
>= (achievementCriteria
->learn_skill_level
.skillLevel
* 75);
1024 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
1025 return progress
->counter
>= 1;
1026 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
1027 return progress
->counter
>= achievementCriteria
->complete_quest_count
.totalQuestCount
;
1028 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
1029 return progress
->counter
>= achievementCriteria
->complete_quests_in_zone
.questCount
;
1030 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
1031 return progress
->counter
>= achievementCriteria
->complete_daily_quest
.questCount
;
1032 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
1033 return progress
->counter
>= achievementCriteria
->fall_without_dying
.fallHeight
;
1034 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
1035 return progress
->counter
>= 1;
1036 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
1037 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
1038 return progress
->counter
>= achievementCriteria
->be_spell_target
.spellCount
;
1039 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
1040 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
1041 return progress
->counter
>= achievementCriteria
->cast_spell
.castCount
;
1042 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
1043 return progress
->counter
>= 1;
1044 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
1045 return progress
->counter
>= achievementCriteria
->own_item
.itemCount
;
1046 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
1047 return progress
->counter
>= achievementCriteria
->use_item
.itemCount
;
1048 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
1049 return progress
->counter
>= achievementCriteria
->loot_item
.itemCount
;
1050 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
1051 return progress
->counter
>= 1;
1052 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
1053 return progress
->counter
>= achievementCriteria
->buy_bank_slot
.numberOfSlots
;
1054 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
1055 return progress
->counter
>= achievementCriteria
->gain_reputation
.reputationAmount
;
1056 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
1057 return progress
->counter
>= achievementCriteria
->gain_exalted_reputation
.numberOfExaltedFactions
;
1058 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
1059 return progress
->counter
>= achievementCriteria
->visit_barber
.numberOfVisits
;
1060 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT
:
1061 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT
:
1062 return progress
->counter
>= achievementCriteria
->roll_greed_on_loot
.count
;
1063 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE
:
1064 return progress
->counter
>= achievementCriteria
->do_emote
.count
;
1065 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
:
1066 return progress
->counter
>= achievementCriteria
->equip_item
.count
;
1067 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
:
1068 return progress
->counter
>= achievementCriteria
->quest_reward_money
.goldInCopper
;
1069 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
:
1070 return progress
->counter
>= achievementCriteria
->loot_money
.goldInCopper
;
1071 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT
:
1072 return progress
->counter
>= achievementCriteria
->use_gameobject
.useCount
;
1073 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
1074 return progress
->counter
>= achievementCriteria
->learn_skilline_spell
.spellCount
;
1076 // handle all statistic-only criteria here
1077 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
1078 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
1079 case ACHIEVEMENT_CRITERIA_TYPE_DEATH
:
1080 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
:
1081 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
1082 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
1083 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
:
1084 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS
:
1085 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER
:
1086 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL
:
1087 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID
:
1088 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD
:
1089 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION
:
1090 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION
:
1091 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS
:
1092 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH
:
1093 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER
:
1094 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR
:
1100 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry
const* criteria
, AchievementEntry
const* achievement
)
1102 // counter can never complete
1103 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
1106 // already completed and stored
1107 if (m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
1110 if ((criteria
->referredAchievement
==achievement
->ID
&& (criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
)) ||
1111 IsCompletedAchievement(achievement
))
1112 CompletedAchievement(achievement
);
1115 // TODO: achievement 705 requires 4 criteria to be fulfilled
1116 bool AchievementMgr::IsCompletedAchievement(AchievementEntry
const* entry
)
1118 // for achievement with referenced achievement criterias get from referenced and counter from self
1119 uint32 achievmentForTestId
= entry
->refAchievement
? entry
->refAchievement
: entry
->ID
;
1120 uint32 achievmentForTestCount
= entry
->count
;
1122 AchievementCriteriaEntryList
const* cList
= achievementmgr
.GetAchievementCriteriaByAchievement(achievmentForTestId
);
1127 bool completed_all
= true;
1129 for(AchievementCriteriaEntryList::const_iterator itr
= cList
->begin(); itr
!= cList
->end(); ++itr
)
1131 AchievementCriteriaEntry
const* criteria
= *itr
;
1133 bool completed
= IsCompletedCriteria(criteria
,entry
);
1135 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1139 completed_all
= false;
1141 if(achievmentForTestId
== entry
->ID
) // not referenced achievement
1143 // completed as single req. criteria
1144 if(completed
&& criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
)
1148 // completed as have req. count of completed criterias
1149 if(achievmentForTestCount
> 0 && achievmentForTestCount
<= count
)
1153 // all criterias completed requirement
1154 if(completed_all
&& achievmentForTestCount
==0)
1160 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry
const* entry
, uint32 changeValue
, ProgressType ptype
)
1162 if((sLog
.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES
)==0)
1163 sLog
.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry
->ID
, changeValue
, m_player
->GetGUIDLow());
1165 CriteriaProgress
*progress
= NULL
;
1167 CriteriaProgressMap::iterator iter
= m_criteriaProgress
.find(entry
->ID
);
1169 if(iter
== m_criteriaProgress
.end())
1171 // not create record for 0 counter
1172 if(changeValue
== 0)
1175 progress
= &m_criteriaProgress
[entry
->ID
];
1176 progress
->counter
= changeValue
;
1177 progress
->date
= time(NULL
);
1181 progress
= &iter
->second
;
1187 newValue
= changeValue
;
1189 case PROGRESS_ACCUMULATE
:
1192 uint32 max_value
= std::numeric_limits
<uint32
>::max();
1193 newValue
= max_value
- progress
->counter
> changeValue
? progress
->counter
+ changeValue
: max_value
;
1196 case PROGRESS_HIGHEST
:
1197 newValue
= progress
->counter
< changeValue
? changeValue
: progress
->counter
;
1201 // not update (not mark as changed) if counter will have same value
1202 if(progress
->counter
== newValue
)
1205 progress
->counter
= newValue
;
1208 progress
->changed
= true;
1210 if(entry
->timeLimit
)
1212 time_t now
= time(NULL
);
1213 if(time_t(progress
->date
+ entry
->timeLimit
) < now
)
1214 progress
->counter
= 1;
1216 // also it seems illogical, the timeframe will be extended at every criteria update
1217 progress
->date
= now
;
1219 SendCriteriaUpdate(entry
->ID
,progress
);
1222 void AchievementMgr::CompletedAchievement(AchievementEntry
const* achievement
)
1224 sLog
.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement
->ID
);
1225 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
|| m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
1228 SendAchievementEarned(achievement
);
1229 CompletedAchievementData
& ca
= m_completedAchievements
[achievement
->ID
];
1230 ca
.date
= time(NULL
);
1233 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1234 // TODO: where do set this instead?
1235 if(!(achievement
->flags
& ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
1236 achievementmgr
.SetRealmCompleted(achievement
);
1238 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
);
1240 // reward items and titles if any
1241 AchievementReward
const* reward
= achievementmgr
.GetAchievementReward(achievement
);
1248 if(uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
?0:1])
1250 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
1251 GetPlayer()->SetTitle(titleEntry
);
1257 Item
* item
= reward
->itemId
? Item::CreateItem(reward
->itemId
,1,GetPlayer ()) : NULL
;
1262 // save new item before send
1263 item
->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1266 mi
.AddItem(item
->GetGUIDLow(), item
->GetEntry(), item
);
1269 int loc_idx
= GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1272 std::string subject
= reward
->subject
;
1273 std::string text
= reward
->text
;
1276 if(AchievementRewardLocale
const* loc
= achievementmgr
.GetAchievementRewardLocale(achievement
))
1278 if (loc
->subject
.size() > size_t(loc_idx
) && !loc
->subject
[loc_idx
].empty())
1279 subject
= loc
->subject
[loc_idx
];
1280 if (loc
->text
.size() > size_t(loc_idx
) && !loc
->text
[loc_idx
].empty())
1281 text
= loc
->text
[loc_idx
];
1285 uint32 itemTextId
= objmgr
.CreateItemText( text
);
1287 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE
, MAIL_STATIONERY_NORMAL
, reward
->sender
, GetPlayer()->GetGUIDLow(), subject
, itemTextId
, &mi
, 0, 0, MAIL_CHECK_MASK_NONE
);
1291 void AchievementMgr::SendAllAchievementData()
1293 // since we don't know the exact size of the packed GUIDs this is just an approximation
1294 WorldPacket
data(SMSG_ALL_ACHIEVEMENT_DATA
, 4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
1295 BuildAllDataPacket(&data
);
1296 GetPlayer()->GetSession()->SendPacket(&data
);
1299 void AchievementMgr::SendRespondInspectAchievements(Player
* player
)
1301 // since we don't know the exact size of the packed GUIDs this is just an approximation
1302 WorldPacket
data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS
, 4+4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
1303 data
.append(GetPlayer()->GetPackGUID());
1304 BuildAllDataPacket(&data
);
1305 player
->GetSession()->SendPacket(&data
);
1309 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1311 void AchievementMgr::BuildAllDataPacket(WorldPacket
*data
)
1313 for(CompletedAchievementMap::const_iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
1315 *data
<< uint32(iter
->first
);
1316 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
1320 for(CriteriaProgressMap::const_iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
1322 *data
<< uint32(iter
->first
);
1323 data
->appendPackGUID(iter
->second
.counter
);
1324 data
->append(GetPlayer()->GetPackGUID());
1326 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
1334 //==========================================================
1335 AchievementCriteriaEntryList
const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type
)
1337 return m_AchievementCriteriasByType
[type
];
1340 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1342 if(sAchievementCriteriaStore
.GetNumRows()==0)
1348 sLog
.outErrorDb(">> Loaded 0 achievement criteria.");
1352 barGoLink
bar( sAchievementCriteriaStore
.GetNumRows() );
1353 for (uint32 entryId
= 0; entryId
< sAchievementCriteriaStore
.GetNumRows(); ++entryId
)
1357 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
1361 m_AchievementCriteriasByType
[criteria
->requiredType
].push_back(criteria
);
1362 m_AchievementCriteriaListByAchievement
[criteria
->referredAchievement
].push_back(criteria
);
1366 sLog
.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType
->size());
1369 void AchievementGlobalMgr::LoadAchievementReferenceList()
1371 if(sAchievementStore
.GetNumRows()==0)
1377 sLog
.outErrorDb(">> Loaded 0 achievement references.");
1382 barGoLink
bar( sAchievementStore
.GetNumRows() );
1383 for (uint32 entryId
= 0; entryId
< sAchievementStore
.GetNumRows(); ++entryId
)
1387 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(entryId
);
1388 if(!achievement
|| !achievement
->refAchievement
)
1391 m_AchievementListByReferencedId
[achievement
->refAchievement
].push_back(achievement
);
1396 sLog
.outString(">> Loaded %u achievement references.",count
);
1399 void AchievementGlobalMgr::LoadCompletedAchievements()
1401 QueryResult
*result
= CharacterDatabase
.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1409 sLog
.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1413 barGoLink
bar(result
->GetRowCount());
1417 Field
*fields
= result
->Fetch();
1418 m_allCompletedAchievements
.insert(fields
[0].GetUInt32());
1419 } while(result
->NextRow());
1424 sLog
.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements
.size());
1427 void AchievementGlobalMgr::LoadRewards()
1429 m_achievementRewards
.clear(); // need for reload case
1432 QueryResult
*result
= WorldDatabase
.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1441 sLog
.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1445 barGoLink
bar(result
->GetRowCount());
1451 Field
*fields
= result
->Fetch();
1452 uint32 entry
= fields
[0].GetUInt32();
1453 if (!sAchievementStore
.LookupEntry(entry
))
1455 sLog
.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry
);
1459 AchievementReward reward
;
1460 reward
.titleId
[0] = fields
[1].GetUInt32();
1461 reward
.titleId
[1] = fields
[2].GetUInt32();
1462 reward
.itemId
= fields
[3].GetUInt32();
1463 reward
.sender
= fields
[4].GetUInt32();
1464 reward
.subject
= fields
[5].GetCppString();
1465 reward
.text
= fields
[6].GetCppString();
1467 if ((reward
.titleId
[0]==0)!=(reward
.titleId
[1]==0))
1468 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]);
1470 // must be title or mail at least
1471 if (!reward
.titleId
[0] && !reward
.titleId
[1] && !reward
.sender
)
1473 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry
);
1477 if (reward
.titleId
[0])
1479 CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(reward
.titleId
[0]);
1482 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry
, reward
.titleId
[0]);
1483 reward
.titleId
[0] = 0;
1487 if (reward
.titleId
[1])
1489 CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(reward
.titleId
[1]);
1492 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry
, reward
.titleId
[1]);
1493 reward
.titleId
[1] = 0;
1497 //check mail data before item for report including wrong item case
1500 if (!objmgr
.GetCreatureTemplate(reward
.sender
))
1502 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry
, reward
.sender
);
1509 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry
);
1511 if (!reward
.subject
.empty())
1512 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry
);
1514 if (!reward
.text
.empty())
1515 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry
);
1520 if (!objmgr
.GetItemPrototype(reward
.itemId
))
1522 sLog
.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry
, reward
.itemId
);
1527 m_achievementRewards
[entry
] = reward
;
1529 } while (result
->NextRow());
1534 sLog
.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales
.size() );
1537 void AchievementGlobalMgr::LoadRewardLocales()
1539 m_achievementRewardLocales
.clear(); // need for reload case
1541 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");
1550 sLog
.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1554 barGoLink
bar(result
->GetRowCount());
1558 Field
*fields
= result
->Fetch();
1561 uint32 entry
= fields
[0].GetUInt32();
1563 if(m_achievementRewards
.find(entry
)==m_achievementRewards
.end())
1565 sLog
.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry
);
1569 AchievementRewardLocale
& data
= m_achievementRewardLocales
[entry
];
1571 for(int i
= 1; i
< MAX_LOCALE
; ++i
)
1573 std::string str
= fields
[1+2*(i
-1)].GetCppString();
1576 int idx
= objmgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
1579 if(data
.subject
.size() <= size_t(idx
))
1580 data
.subject
.resize(idx
+1);
1582 data
.subject
[idx
] = str
;
1585 str
= fields
[1+2*(i
-1)+1].GetCppString();
1588 int idx
= objmgr
.GetOrNewIndexForLocale(LocaleConstant(i
));
1591 if(data
.text
.size() <= size_t(idx
))
1592 data
.text
.resize(idx
+1);
1594 data
.text
[idx
] = str
;
1598 } while (result
->NextRow());
1603 sLog
.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales
.size() );