Implement support of Timed Achievement. Thanks Shauren.
Fixes issue #1431. --HG-- branch : trunk
This commit is contained in:
+117
-16
@@ -611,6 +611,10 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
|
||||
if (GetPlayer()->GetSession()->PlayerLoading())
|
||||
return;
|
||||
|
||||
// Don't send for achievements with ACHIEVEMENT_FLAG_TRACKING
|
||||
if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING)
|
||||
return;
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0)
|
||||
sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
|
||||
@@ -657,19 +661,22 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
|
||||
GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
|
||||
}
|
||||
|
||||
void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
|
||||
void AchievementMgr::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted)
|
||||
{
|
||||
WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
|
||||
data << uint32(id);
|
||||
data << uint32(entry->ID);
|
||||
|
||||
// the counter is packed like a packed Guid
|
||||
data.appendPackGUID(progress->counter);
|
||||
|
||||
data.append(GetPlayer()->GetPackGUID());
|
||||
if (!entry->timeLimit)
|
||||
data << uint32(0);
|
||||
else
|
||||
data << uint32(timedCompleted ? 0 : 1); // this are some flags, 1 is for keeping the counter at 0 in client
|
||||
data << uint32(secsToTimeBitFields(progress->date));
|
||||
data << uint32(0); // timer 1
|
||||
data << uint32(0); // timer 2
|
||||
data << uint32(timeElapsed); // time elapsed in seconds
|
||||
data << uint32(0); // unk
|
||||
GetPlayer()->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
@@ -711,9 +718,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
|
||||
{
|
||||
AchievementCriteriaEntry const *achievementCriteria = (*i);
|
||||
|
||||
if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
|
||||
continue;
|
||||
|
||||
AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
|
||||
if (!achievement)
|
||||
continue;
|
||||
@@ -1695,6 +1699,11 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
|
||||
|
||||
void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
|
||||
{
|
||||
// Don't allow to cheat - doing timed achievements without timer active
|
||||
TimedAchievementMap::iterator timedIter = m_timedAchievements.find(entry->ID);
|
||||
if (entry->timeLimit && timedIter == m_timedAchievements.end())
|
||||
return;
|
||||
|
||||
if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0)
|
||||
sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
|
||||
|
||||
@@ -1704,8 +1713,9 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
|
||||
|
||||
if (iter == m_criteriaProgress.end())
|
||||
{
|
||||
// not create record for 0 counter
|
||||
if (changeValue == 0)
|
||||
// not create record for 0 counter but allow it for timed achievements
|
||||
// we will need to send 0 progress to client to start the timer
|
||||
if (changeValue == 0 && !entry->timeLimit)
|
||||
return;
|
||||
|
||||
progress = &m_criteriaProgress[entry->ID];
|
||||
@@ -1735,7 +1745,7 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
|
||||
}
|
||||
|
||||
// not update (not mark as changed) if counter will have same value
|
||||
if (progress->counter == newValue)
|
||||
if (progress->counter == newValue && !entry->timeLimit)
|
||||
return;
|
||||
|
||||
progress->counter = newValue;
|
||||
@@ -1743,16 +1753,85 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
|
||||
|
||||
progress->changed = true;
|
||||
|
||||
uint32 timeElapsed = 0;
|
||||
bool timedCompleted = false;
|
||||
|
||||
if (entry->timeLimit)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if (time_t(progress->date + entry->timeLimit) < now)
|
||||
progress->counter = 1;
|
||||
//has to exist else we wouldn't be here
|
||||
timedCompleted = IsCompletedCriteria(entry, sAchievementStore.LookupEntry(entry->referredAchievement));
|
||||
// Client expects this in packet
|
||||
timeElapsed = entry->timeLimit - (timedIter->second/IN_MILISECONDS);
|
||||
|
||||
// also it seems illogical, the timeframe will be extended at every criteria update
|
||||
progress->date = now;
|
||||
// Remove the timer, we wont need it anymore
|
||||
if (timedCompleted)
|
||||
m_timedAchievements.erase(timedIter);
|
||||
}
|
||||
|
||||
SendCriteriaUpdate(entry, progress, timeElapsed, timedCompleted);
|
||||
}
|
||||
|
||||
void AchievementMgr::UpdateTimedAchievements(uint32 timeDiff)
|
||||
{
|
||||
if (!m_timedAchievements.empty())
|
||||
{
|
||||
for (TimedAchievementMap::iterator itr = m_timedAchievements.begin(); itr != m_timedAchievements.end();)
|
||||
{
|
||||
// Time is up, remove timer and reset progress
|
||||
if (itr->second <= timeDiff)
|
||||
{
|
||||
AchievementCriteriaEntry const *entry = sAchievementCriteriaStore.LookupEntry(itr->first);
|
||||
SetCriteriaProgress(entry, 0, PROGRESS_SET);
|
||||
m_timedAchievements.erase(itr++);
|
||||
}
|
||||
else
|
||||
{
|
||||
itr->second -= timeDiff;
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementMgr::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
{
|
||||
AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type);
|
||||
for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i)
|
||||
{
|
||||
if ((*i)->timerStartEvent != entry)
|
||||
continue;
|
||||
|
||||
AchievementEntry const *achievement = sAchievementStore.LookupEntry((*i)->referredAchievement);
|
||||
if (m_timedAchievements.find((*i)->ID) == m_timedAchievements.end() && !IsCompletedCriteria(*i, achievement))
|
||||
{
|
||||
// Start the timer
|
||||
m_timedAchievements[(*i)->ID] = (*i)->timeLimit * IN_MILISECONDS;
|
||||
|
||||
// and at client too
|
||||
SetCriteriaProgress(*i, 0, PROGRESS_SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementMgr::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
{
|
||||
AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type);
|
||||
for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
|
||||
{
|
||||
if ((*i)->timerStartEvent != entry)
|
||||
continue;
|
||||
|
||||
TimedAchievementMap::iterator timedIter = m_timedAchievements.find((*i)->ID);
|
||||
// We don't have timer for this achievement
|
||||
if (timedIter == m_timedAchievements.end())
|
||||
continue;
|
||||
|
||||
// 0 the progress to avoid saving to db
|
||||
SetCriteriaProgress(*i, 0, PROGRESS_SET);
|
||||
|
||||
// Remove the timer
|
||||
m_timedAchievements.erase(timedIter);
|
||||
}
|
||||
SendCriteriaUpdate(entry->ID,progress);
|
||||
}
|
||||
|
||||
void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
|
||||
@@ -1847,8 +1926,14 @@ void AchievementMgr::SendRespondInspectAchievements(Player* player)
|
||||
*/
|
||||
void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
|
||||
{
|
||||
AchievementEntry const *achievement;
|
||||
for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter)
|
||||
{
|
||||
// Skip tracking - they bug client UI
|
||||
achievement = sAchievementStore.LookupEntry(iter->first);
|
||||
if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING)
|
||||
continue;
|
||||
|
||||
*data << uint32(iter->first);
|
||||
*data << uint32(secsToTimeBitFields(iter->second.date));
|
||||
}
|
||||
@@ -1879,6 +1964,11 @@ AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteria
|
||||
return m_AchievementCriteriasByType[type];
|
||||
}
|
||||
|
||||
AchievementCriteriaEntryList const& AchievementGlobalMgr::GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type)
|
||||
{
|
||||
return m_AchievementCriteriasByTimedType[type];
|
||||
}
|
||||
|
||||
void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
{
|
||||
if (sAchievementCriteriaStore.GetNumRows() == 0)
|
||||
@@ -1902,6 +1992,9 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
|
||||
m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
|
||||
m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
|
||||
|
||||
if(criteria->timeLimit)
|
||||
m_AchievementCriteriasByTimedType[criteria->timedType].push_back(criteria);
|
||||
}
|
||||
|
||||
sLog.outString();
|
||||
@@ -2030,6 +2123,8 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
|
||||
}
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
|
||||
break; // any cases
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: // any cases
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
|
||||
@@ -2038,10 +2133,16 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: // any cases
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
|
||||
break; // any cases
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
|
||||
break; // any cases
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
|
||||
if (criteria->do_emote.count == 0)
|
||||
continue;
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
|
||||
break; // any cases
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
|
||||
if (criteria->win_duel.duelCount == 0)
|
||||
continue;
|
||||
|
||||
@@ -250,11 +250,14 @@ class AchievementMgr
|
||||
void SendRespondInspectAchievements(Player* player);
|
||||
bool HasAchieved(AchievementEntry const* achievement) const;
|
||||
Player* GetPlayer() { return m_player; }
|
||||
void UpdateTimedAchievements(uint32 timeDiff);
|
||||
void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry);
|
||||
void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); // used for quest and scripted timed achievements
|
||||
|
||||
private:
|
||||
enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST };
|
||||
void SendAchievementEarned(AchievementEntry const* achievement);
|
||||
void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress);
|
||||
void SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted);
|
||||
void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET);
|
||||
void CompletedCriteriaFor(AchievementEntry const* achievement);
|
||||
bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement);
|
||||
@@ -265,12 +268,15 @@ class AchievementMgr
|
||||
Player* m_player;
|
||||
CriteriaProgressMap m_criteriaProgress;
|
||||
CompletedAchievementMap m_completedAchievements;
|
||||
typedef std::map<uint32, uint32> TimedAchievementMap;
|
||||
TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS
|
||||
};
|
||||
|
||||
class AchievementGlobalMgr
|
||||
{
|
||||
public:
|
||||
AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type);
|
||||
AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type);
|
||||
AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id)
|
||||
{
|
||||
AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id);
|
||||
@@ -322,6 +328,7 @@ class AchievementGlobalMgr
|
||||
|
||||
// store achievement criterias by type to speed up lookup
|
||||
AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
|
||||
AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX];
|
||||
// store achievement criterias by achievement to speed up lookup
|
||||
AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement;
|
||||
// store achievements by referenced achievement id to speed up lookup
|
||||
|
||||
+10
-4
@@ -58,7 +58,7 @@ enum AchievementFaction
|
||||
enum AchievementFlags
|
||||
{
|
||||
ACHIEVEMENT_FLAG_COUNTER = 0x00000001, // Just count statistic (never stop and complete)
|
||||
ACHIEVEMENT_FLAG_UNK2 = 0x00000002, // not used
|
||||
ACHIEVEMENT_FLAG_TRACKING = 0x00000002, // Not sent to client - internal use only
|
||||
ACHIEVEMENT_FLAG_STORE_MAX_VALUE = 0x00000004, // Store only max value? used only in "Reach level xx"
|
||||
ACHIEVEMENT_FLAG_SUMM = 0x00000008, // Use summ criteria value from all reqirements (and calculate max value)
|
||||
ACHIEVEMENT_FLAG_MAX_USED = 0x00000010, // Show max criteria (and calculate max value ??)
|
||||
@@ -90,10 +90,16 @@ enum AchievementCriteriaCompletionFlags
|
||||
ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER = 0x00000020, // Displays counter as money
|
||||
};
|
||||
|
||||
enum AchievementCriteriaGroupFlags
|
||||
enum AchievementCriteriaTimedTypes
|
||||
{
|
||||
// you mustn't be in a group while fulfilling this achievement
|
||||
ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP = 2,
|
||||
ACHIEVEMENT_TIMED_TYPE_EVENT = 1, // Timer is started by internal event with id in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_QUEST = 2, // Timer is started by acceting quest with entry in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_SPELL_CASTER = 5, // Timer is started by casting a spell with entry in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET = 6, // Timer is started by being target of spell with entry in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_CREATURE = 7, // Timer is started by killing creature with entry in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_ITEM = 9, // Timer is started by using item with entry in timerStartEvent
|
||||
|
||||
ACHIEVEMENT_TIMED_TYPE_MAX,
|
||||
};
|
||||
|
||||
enum AchievementCriteriaTypes
|
||||
|
||||
@@ -492,8 +492,8 @@ struct AchievementCriteriaEntry
|
||||
//char* name[16]; // 9-24
|
||||
//uint32 name_flags; // 25
|
||||
uint32 completionFlag; // 26
|
||||
uint32 groupFlag; // 27
|
||||
//uint32 unk1; // 28 Alway appears with timed events
|
||||
uint32 timedType; // 27
|
||||
uint32 timerStartEvent; // 28 Alway appears with timed events
|
||||
// for timed spells it is spell id for
|
||||
// timed kills it is creature id
|
||||
uint32 timeLimit; // 29 time limit in seconds
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#define TRINITY_DBCSFRM_H
|
||||
|
||||
const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
|
||||
const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix";
|
||||
const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix";
|
||||
const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx";
|
||||
const char AreaGroupEntryfmt[]="niiiiiii";
|
||||
const char AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix";
|
||||
|
||||
@@ -1218,6 +1218,8 @@ void Player::Update(uint32 p_time)
|
||||
}
|
||||
}
|
||||
|
||||
GetAchievementMgr().UpdateTimedAchievements(p_time);
|
||||
|
||||
if (hasUnitState(UNIT_STAT_MELEE_ATTACKING) && !hasUnitState(UNIT_STAT_CASTING))
|
||||
{
|
||||
if (Unit *pVictim = getVictim())
|
||||
@@ -13975,6 +13977,8 @@ void Player::AddQuest(Quest const *pQuest, Object *questGiver)
|
||||
if (questStatusData.uState != QUEST_NEW)
|
||||
questStatusData.uState = QUEST_CHANGED;
|
||||
|
||||
GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
|
||||
|
||||
//starting initial quest script
|
||||
if (questGiver && pQuest->GetQuestStartScript() != 0)
|
||||
GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
|
||||
@@ -14899,7 +14903,9 @@ void Player::KilledMonsterCredit(uint32 entry, uint64 guid)
|
||||
real_entry = killed->GetEntry();
|
||||
}
|
||||
|
||||
GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_CREATURE, real_entry); // MUST BE CALLED FIRST
|
||||
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, real_entry, addkillcount);
|
||||
|
||||
for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||
{
|
||||
uint32 questid = GetQuestSlotQuestId(i);
|
||||
|
||||
@@ -392,6 +392,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data)
|
||||
|
||||
_player->TakeQuestSourceItem(quest, true); // remove quest src item from player
|
||||
_player->SetQuestStatus(quest, QUEST_STATUS_NONE);
|
||||
_player->GetAchievementMgr().RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest);
|
||||
}
|
||||
|
||||
_player->SetQuestSlot(slot, 0);
|
||||
|
||||
@@ -1266,12 +1266,14 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
|
||||
|
||||
if (unit->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
unit->ToPlayer()->GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, m_spellInfo->Id);
|
||||
unit->ToPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id);
|
||||
unit->ToPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id);
|
||||
}
|
||||
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
m_caster->ToPlayer()->GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_CASTER, m_spellInfo->Id);
|
||||
m_caster->ToPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit);
|
||||
}
|
||||
|
||||
@@ -2929,7 +2931,10 @@ void Spell::cast(bool skipCheck)
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (!m_IsTriggeredSpell && m_CastItem)
|
||||
{
|
||||
m_caster->ToPlayer()->GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_ITEM, m_CastItem->GetEntry());
|
||||
m_caster->ToPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry());
|
||||
}
|
||||
|
||||
m_caster->ToPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user