Core/Achievements: ported start event and fail event mechanics from master branch
This commit is contained in:
1 parent
a599f7461d
commit
ff0eb86c54
41 files changed
+325
-362
No files matched your search
@@ -0,0 +1 @@
|
||||
DELETE FROM `spell_script_names` WHERE `ScriptName`='spell_algalon_supermassive_fail';
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Battleground.h"
|
||||
#include "CellImpl.h"
|
||||
#include "ChatTextBuilder.h"
|
||||
#include "Containers.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DBCEnums.h"
|
||||
#include "DisableMgr.h"
|
||||
@@ -44,6 +45,7 @@
|
||||
#include "SpellMgr.h"
|
||||
#include "World.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldStateMgr.h"
|
||||
|
||||
bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
|
||||
{
|
||||
@@ -469,7 +471,7 @@ void AchievementMgr<Guild>::RemoveCriteriaProgress(AchievementCriteriaEntry cons
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
CriteriaProgressMap::iterator criteriaProgress = m_criteriaProgress.find(entry->ID);
|
||||
auto criteriaProgress = m_criteriaProgress.find(entry->ID);
|
||||
if (criteriaProgress == m_criteriaProgress.end())
|
||||
return;
|
||||
|
||||
@@ -497,43 +499,11 @@ void AchievementMgr<Guild>::RemoveCriteriaProgress(AchievementCriteriaEntry cons
|
||||
|
||||
SendPacket(&data);
|
||||
|
||||
criteriaProgress->second.counter = 0;
|
||||
criteriaProgress->second.changed = true;
|
||||
m_criteriaProgress.erase(criteriaProgress);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void AchievementMgr<T>::ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete)
|
||||
{
|
||||
TC_LOG_DEBUG("achievement", "ResetAchievementCriteria(%u, " UI64FMTD ", %u)", condition, value, evenIfCriteriaComplete);
|
||||
|
||||
// disable for gamemasters with GM-mode enabled
|
||||
if (GetOwner()->IsGameMaster())
|
||||
return;
|
||||
|
||||
AchievementCriteriaEntryList const* achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByCondition(condition, value);
|
||||
if (!achievementCriteriaList)
|
||||
return;
|
||||
|
||||
for (auto itr = achievementCriteriaList->begin(); itr != achievementCriteriaList->end(); ++itr)
|
||||
{
|
||||
AchievementCriteriaEntry const* achievementCriteria = *itr;
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->ReferredAchievement);
|
||||
if (!achievement)
|
||||
continue;
|
||||
|
||||
// don't update already completed criteria if not forced or achievement already complete
|
||||
if ((IsCompletedCriteria(achievementCriteria, achievement) && !evenIfCriteriaComplete) || HasAchieved(achievement->ID))
|
||||
continue;
|
||||
|
||||
RemoveCriteriaProgress(achievementCriteria);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void AchievementMgr<Guild>::ResetAchievementCriteria(AchievementCriteriaCondition /*condition*/, uint64 /*value*/, bool /*evenIfCriteriaComplete*/)
|
||||
{
|
||||
// Not needed
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void AchievementMgr<T>::DeleteFromDB(ObjectGuid /*guid*/)
|
||||
{
|
||||
@@ -734,7 +704,7 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P
|
||||
continue;
|
||||
}
|
||||
|
||||
if (criteria->StartTimer && time_t(date + criteria->StartTimer) < GameTime::GetGameTime())
|
||||
if (criteria->TimerTime && time_t(date + criteria->TimerTime) < GameTime::GetGameTime())
|
||||
continue;
|
||||
|
||||
CriteriaProgress& progress = m_criteriaProgress[id];
|
||||
@@ -796,7 +766,7 @@ void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, Pr
|
||||
continue;
|
||||
}
|
||||
|
||||
if (criteria->StartTimer && time_t(date + criteria->StartTimer) < GameTime::GetGameTime())
|
||||
if (criteria->TimerTime && time_t(date + criteria->TimerTime) < GameTime::GetGameTime())
|
||||
continue;
|
||||
|
||||
CriteriaProgress& progress = m_criteriaProgress[id];
|
||||
@@ -961,7 +931,7 @@ void AchievementMgr<Player>::SendCriteriaUpdate(AchievementCriteriaEntry const*
|
||||
criteriaUpdate.Quantity = progress->counter;
|
||||
criteriaUpdate.PlayerGUID = GetOwner()->GetGUID();
|
||||
criteriaUpdate.Flags = 0;
|
||||
if (entry->StartTimer)
|
||||
if (entry->TimerTime)
|
||||
criteriaUpdate.Flags = timedCompleted ? 1 : 0; // 1 is for keeping the counter at 0 in client
|
||||
|
||||
criteriaUpdate.CurrentTime = progress->date;
|
||||
@@ -1179,10 +1149,10 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
|
||||
for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i)
|
||||
{
|
||||
AchievementCriteriaEntry const* achievementCriteria = (*i);
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->ReferredAchievement);
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->AchievementID);
|
||||
if (!achievement)
|
||||
{
|
||||
TC_LOG_ERROR("achievement", "UpdateAchievementCriteria: Achievement %u not found!", achievementCriteria->ReferredAchievement);
|
||||
TC_LOG_ERROR("achievement", "UpdateAchievementCriteria: Achievement %u not found!", achievementCriteria->AchievementID);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1619,18 +1589,18 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteriaEntry const* achi
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CRAFT_ITEMS_GUILD:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CATCH_FROM_POOL:
|
||||
return progress->counter >= achievementCriteria->Amount;
|
||||
return progress->counter >= achievementCriteria->Quantity;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BUY_GUILD_TABARD:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
|
||||
return progress->counter >= 1;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
|
||||
return progress->counter >= achievementCriteria->Amount;
|
||||
return progress->counter >= achievementCriteria->Quantity;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS:
|
||||
return progress->counter >= 9000;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
|
||||
return achievementCriteria->Amount && progress->counter >= achievementCriteria->Amount;
|
||||
return achievementCriteria->Quantity && progress->counter >= achievementCriteria->Quantity;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
|
||||
return true;
|
||||
// handle all statistic-only criteria here
|
||||
@@ -1716,7 +1686,7 @@ bool AchievementMgr<T>::IsCompletedAchievement(AchievementEntry const* entry)
|
||||
count += progress->counter;
|
||||
|
||||
// for counters, field4 contains the main count requirement
|
||||
if (count >= criteria->Amount)
|
||||
if (count >= criteria->Quantity)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1762,11 +1732,6 @@ CriteriaProgress* AchievementMgr<T>::GetCriteriaProgress(AchievementCriteriaEntr
|
||||
template<class T>
|
||||
void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint64 changeValue, Player* referencePlayer, ProgressType ptype)
|
||||
{
|
||||
// Don't allow to cheat - doing timed achievements without timer active
|
||||
TimedAchievementMap::iterator timedIter = m_timedAchievements.find(entry->ID);
|
||||
if (entry->StartTimer && timedIter == m_timedAchievements.end())
|
||||
return;
|
||||
|
||||
TC_LOG_DEBUG("achievement", "SetCriteriaProgress(%u, " UI64FMTD ") for (%s)",
|
||||
entry->ID, changeValue, GetOwner()->GetGUID().ToString().c_str());
|
||||
|
||||
@@ -1775,7 +1740,7 @@ void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entr
|
||||
{
|
||||
// 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->StartTimer)
|
||||
if (changeValue == 0 && !entry->TimerTime)
|
||||
return;
|
||||
|
||||
progress = &m_criteriaProgress[entry->ID];
|
||||
@@ -1802,7 +1767,7 @@ void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entr
|
||||
}
|
||||
|
||||
// not update (not mark as changed) if counter will have same value
|
||||
if (progress->counter == newValue && !entry->StartTimer)
|
||||
if (progress->counter == newValue && !entry->TimerTime)
|
||||
return;
|
||||
|
||||
progress->counter = newValue;
|
||||
@@ -1810,98 +1775,102 @@ void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entr
|
||||
|
||||
progress->changed = true;
|
||||
progress->date = GameTime::GetGameTime(); // set the date to the latest update.
|
||||
progress->CompletedGUID = referencePlayer ? referencePlayer->GetGUID() : ObjectGuid::Empty;
|
||||
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(entry->ReferredAchievement);
|
||||
uint32 timeElapsed = 0;
|
||||
bool criteriaComplete = IsCompletedCriteria(entry, achievement);
|
||||
Seconds timeElapsed = Seconds::zero();
|
||||
|
||||
if (entry->StartTimer)
|
||||
if (entry->TimerTime)
|
||||
{
|
||||
// Client expects this in packet
|
||||
timeElapsed = entry->StartTimer - (timedIter->second/IN_MILLISECONDS);
|
||||
auto startedItr = _startedCriteria.find(entry->ID);
|
||||
if (startedItr != _startedCriteria.end())
|
||||
{
|
||||
// Client expects this in packet
|
||||
timeElapsed = duration_cast<Seconds>(Seconds(entry->TimerTime) - startedItr->second);
|
||||
|
||||
// Remove the timer, we wont need it anymore
|
||||
if (criteriaComplete)
|
||||
m_timedAchievements.erase(timedIter);
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(entry->AchievementID);
|
||||
bool criteriaComplete = IsCompletedCriteria(entry, achievement);
|
||||
// Remove the timer, we wont need it anymore
|
||||
if (criteriaComplete)
|
||||
_startedCriteria.erase(startedItr);
|
||||
}
|
||||
}
|
||||
|
||||
if (criteriaComplete && achievement->Flags & ACHIEVEMENT_FLAG_SHOW_CRITERIA_MEMBERS && !progress->CompletedGUID)
|
||||
progress->CompletedGUID = referencePlayer->GetGUID();
|
||||
|
||||
SendCriteriaUpdate(entry, progress, timeElapsed, criteriaComplete);
|
||||
SendCriteriaUpdate(entry, progress, timeElapsed.count(), true);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void AchievementMgr<T>::UpdateTimedAchievements(uint32 timeDiff)
|
||||
void AchievementMgr<T>::UpdateTimedAchievementCriteria(Milliseconds timeDiff)
|
||||
{
|
||||
if (!m_timedAchievements.empty())
|
||||
for (auto itr = _startedCriteria.begin(); itr != _startedCriteria.end();)
|
||||
{
|
||||
for (TimedAchievementMap::iterator itr = m_timedAchievements.begin(); itr != m_timedAchievements.end();)
|
||||
// Time is up, remove timer and reset progress
|
||||
if (itr->second <= timeDiff)
|
||||
{
|
||||
// Time is up, remove timer and reset progress
|
||||
if (itr->second <= timeDiff)
|
||||
{
|
||||
AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteria(itr->first);
|
||||
RemoveCriteriaProgress(entry);
|
||||
m_timedAchievements.erase(itr++);
|
||||
}
|
||||
else
|
||||
{
|
||||
itr->second -= timeDiff;
|
||||
++itr;
|
||||
}
|
||||
AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteria(itr->first);
|
||||
RemoveCriteriaProgress(entry);
|
||||
itr = _startedCriteria.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
itr->second -= timeDiff;
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void AchievementMgr<T>::StartTimedAchievement(AchievementCriteriaTimedTypes /*type*/, uint32 /*entry*/, uint32 /*timeLost = 0*/)
|
||||
void AchievementMgr<T>::StartAchievementCriteria(AchievementCriteriaStartEvent /*startEvent*/, uint32 /*entry*/, Milliseconds /*timeLost = Milliseconds::zero() */)
|
||||
{
|
||||
}
|
||||
|
||||
template<>
|
||||
void AchievementMgr<Player>::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost /* = 0 */)
|
||||
void AchievementMgr<Player>::StartAchievementCriteria(AchievementCriteriaStartEvent startEvent, uint32 asset, Milliseconds timeLost /*= Milliseconds::zero()*/)
|
||||
{
|
||||
AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type);
|
||||
for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i)
|
||||
AchievementCriteriaEntryList const* criteriaList = sAchievementMgr->GetAchievementCriteriaByStartEvent(startEvent, asset);
|
||||
if (!criteriaList)
|
||||
return;
|
||||
|
||||
for (AchievementCriteriaEntry const* criteria : *criteriaList)
|
||||
{
|
||||
if ((*i)->StartAsset != entry)
|
||||
Milliseconds timeLimit = Milliseconds::max(); // this value is for criteria that have a start event requirement but no time limit
|
||||
if (criteria->TimerTime)
|
||||
timeLimit = Seconds(criteria->TimerTime);
|
||||
|
||||
timeLimit -= timeLost;
|
||||
|
||||
if (timeLimit <= Milliseconds::zero())
|
||||
continue;
|
||||
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement((*i)->ReferredAchievement);
|
||||
if (m_timedAchievements.find((*i)->ID) == m_timedAchievements.end() && !IsCompletedCriteria(*i, achievement))
|
||||
{
|
||||
// Start the timer
|
||||
if ((*i)->StartTimer * IN_MILLISECONDS > timeLost)
|
||||
{
|
||||
m_timedAchievements[(*i)->ID] = (*i)->StartTimer * IN_MILLISECONDS - timeLost;
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(criteria->AchievementID);
|
||||
bool canStart = !IsCompletedCriteria(criteria, achievement);
|
||||
|
||||
// and at client too
|
||||
SetCriteriaProgress(*i, 0, GetOwner(), PROGRESS_SET);
|
||||
}
|
||||
if (!canStart)
|
||||
continue;
|
||||
|
||||
auto [itr, isNew] = _startedCriteria.try_emplace(criteria->ID, timeLimit);
|
||||
if (!isNew)
|
||||
{
|
||||
if (!(criteria->Flags & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN))
|
||||
continue;
|
||||
|
||||
itr->second = timeLimit;
|
||||
}
|
||||
|
||||
SetCriteriaProgress(criteria, 0, GetOwner(), PROGRESS_SET);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void AchievementMgr<T>::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
void AchievementMgr<T>::FailAchievementCriteria(AchievementCriteriaFailEvent failEvent, uint32 asset)
|
||||
{
|
||||
AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type);
|
||||
for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i)
|
||||
AchievementCriteriaEntryList const* criteriaList = sAchievementMgr->GetAchievementCriteriaByFailEvent(failEvent, asset);
|
||||
if (!criteriaList)
|
||||
return;
|
||||
|
||||
for (AchievementCriteriaEntry const* criteria : *criteriaList)
|
||||
{
|
||||
if ((*i)->StartAsset != entry)
|
||||
continue;
|
||||
|
||||
TimedAchievementMap::iterator timedIter = m_timedAchievements.find((*i)->ID);
|
||||
// We don't have timer for this achievement
|
||||
if (timedIter == m_timedAchievements.end())
|
||||
continue;
|
||||
|
||||
// remove progress
|
||||
RemoveCriteriaProgress(*i);
|
||||
|
||||
// Remove the timer
|
||||
m_timedAchievements.erase(timedIter);
|
||||
_startedCriteria.erase(criteria->ID);
|
||||
RemoveCriteriaProgress(criteria);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2360,13 +2329,6 @@ bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteriaEntry const* criter
|
||||
return false;
|
||||
}
|
||||
|
||||
if (achievement->MapID != -1 && referencePlayer->GetMapId() != uint32(achievement->MapID))
|
||||
{
|
||||
TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Wrong map",
|
||||
criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Type), achievement->ID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) ||
|
||||
(achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE))
|
||||
{
|
||||
@@ -2403,36 +2365,18 @@ bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteriaEntry const* criter
|
||||
return false;
|
||||
}
|
||||
|
||||
if (criteria->RequiredWorldStateID != 0)
|
||||
if (sWorldStateMgr->GetValue(criteria->RequiredWorldStateID, referencePlayer->GetMap()) != criteria->RequiredWorldStateValue)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool AchievementMgr<T>::ConditionsSatisfied(AchievementCriteriaEntry const* criteria, Player* referencePlayer) const
|
||||
bool AchievementMgr<T>::ConditionsSatisfied(AchievementCriteriaEntry const* criteria, Player* /*referencePlayer*/) const
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i)
|
||||
{
|
||||
if (!criteria->AdditionalRequirements[i].Type)
|
||||
continue;
|
||||
|
||||
switch (criteria->AdditionalRequirements[i].Type)
|
||||
{
|
||||
case ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP:
|
||||
if (referencePlayer->GetMapId() != criteria->AdditionalRequirements[i].Asset)
|
||||
return false;
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_CONDITION_NOT_IN_GROUP:
|
||||
if (referencePlayer->GetGroup())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (criteria->RequiredWorldStateID != 0)
|
||||
if (Map* map = referencePlayer->GetMap())
|
||||
if (map->GetWorldStateValue(criteria->RequiredWorldStateID) != criteria->RequiredWorldStateValue)
|
||||
return false;
|
||||
if (criteria->StartEvent && !_startedCriteria.contains(criteria->ID))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2548,7 +2492,7 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac
|
||||
bool notfit = false;
|
||||
for (int j = 0; j < MAX_ARENA_SLOT; ++j)
|
||||
{
|
||||
if (achievIdByArenaSlot[j] == achievementCriteria->ReferredAchievement)
|
||||
if (achievIdByArenaSlot[j] == achievementCriteria->AchievementID)
|
||||
{
|
||||
Battleground* bg = referencePlayer->GetBattleground();
|
||||
if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
|
||||
@@ -2573,7 +2517,7 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac
|
||||
bool found = false;
|
||||
for (int j = 0; achievIdForDungeon[j][0]; ++j)
|
||||
{
|
||||
if (achievIdForDungeon[j][0] == achievementCriteria->ReferredAchievement)
|
||||
if (achievIdForDungeon[j][0] == achievementCriteria->AchievementID)
|
||||
{
|
||||
if (map->IsRaid())
|
||||
{
|
||||
@@ -2736,9 +2680,9 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac
|
||||
if (!miscValue1)
|
||||
return false;
|
||||
|
||||
if (achievementCriteria->AdditionalRequirements[0].Type == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP)
|
||||
if (AchievementCriteriaFailEvent(achievementCriteria->FailEvent) == AchievementCriteriaFailEvent::LeaveBattleground)
|
||||
{
|
||||
if (referencePlayer->GetMapId() != achievementCriteria->AdditionalRequirements[0].Asset)
|
||||
if (!referencePlayer->InBattleground())
|
||||
return false;
|
||||
|
||||
// map specific case (BG in fact) expected player targeted damage/heal
|
||||
@@ -2979,7 +2923,7 @@ bool AchievementMgr<T>::AdditionalRequirementsSatisfied(AchievementCriteriaEntry
|
||||
}
|
||||
case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_COMPLETE_QUEST_NOT_IN_GROUP: // 35
|
||||
{
|
||||
if (referencePlayer->IsInGroup())
|
||||
if (referencePlayer->GetGroup())
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -3377,6 +3321,16 @@ AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteria
|
||||
}
|
||||
}
|
||||
|
||||
AchievementCriteriaEntryList const* AchievementGlobalMgr::GetAchievementCriteriaByStartEvent(AchievementCriteriaStartEvent startEvent, int32 asset) const
|
||||
{
|
||||
return Trinity::Containers::MapGetValuePtr(_criteriasByStartEvent[size_t(startEvent)], asset);
|
||||
}
|
||||
|
||||
AchievementCriteriaEntryList const* AchievementGlobalMgr::GetAchievementCriteriaByFailEvent(AchievementCriteriaFailEvent startEvent, int32 asset) const
|
||||
{
|
||||
return Trinity::Containers::MapGetValuePtr(_criteriasByFailEvent[size_t(startEvent)], asset);
|
||||
}
|
||||
|
||||
bool AchievementGlobalMgr::IsRealmCompleted(AchievementEntry const* achievement) const
|
||||
{
|
||||
auto itr = _allCompletedAchievements.find(achievement->ID);
|
||||
@@ -3406,6 +3360,21 @@ void AchievementGlobalMgr::SetRealmCompleted(AchievementEntry const* achievement
|
||||
_allCompletedAchievements[achievement->ID] = GameTime::GetGameTimeSystemPoint();
|
||||
}
|
||||
|
||||
inline AchievementCriteriaStartEvent ToStartEvent(uint32 timedStartEvent)
|
||||
{
|
||||
switch (static_cast<AchievementCriteriaTimerStartEvent>(timedStartEvent))
|
||||
{
|
||||
case AchievementCriteriaTimerStartEvent::SendEvent: return AchievementCriteriaStartEvent::SendEvent;
|
||||
case AchievementCriteriaTimerStartEvent::AcceptQuest: return AchievementCriteriaStartEvent::AcceptQuest;
|
||||
case AchievementCriteriaTimerStartEvent::CastSpell: return AchievementCriteriaStartEvent::CastSpell;
|
||||
case AchievementCriteriaTimerStartEvent::GainAura: return AchievementCriteriaStartEvent::GainAura;
|
||||
case AchievementCriteriaTimerStartEvent::KillNPC: return AchievementCriteriaStartEvent::KillNPC;
|
||||
case AchievementCriteriaTimerStartEvent::UseItem: return AchievementCriteriaStartEvent::UseItem;
|
||||
default:
|
||||
return AchievementCriteriaStartEvent::None;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
{
|
||||
@@ -3428,9 +3397,9 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
ASSERT(criteria->Type < ACHIEVEMENT_CRITERIA_TYPE_TOTAL, "ACHIEVEMENT_CRITERIA_TYPE_TOTAL must be greater than or equal to %u but is currently equal to %u",
|
||||
criteria->Type + 1, ACHIEVEMENT_CRITERIA_TYPE_TOTAL);
|
||||
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(criteria->ReferredAchievement);
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(criteria->AchievementID);
|
||||
|
||||
m_AchievementCriteriaListByAchievement[criteria->ReferredAchievement].push_back(criteria);
|
||||
m_AchievementCriteriaListByAchievement[criteria->AchievementID].push_back(criteria);
|
||||
|
||||
bool guild = false;
|
||||
if (achievement && achievement->Flags & ACHIEVEMENT_FLAG_GUILD)
|
||||
@@ -3466,27 +3435,19 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i)
|
||||
{
|
||||
if (criteria->AdditionalRequirements[i].Type != ACHIEVEMENT_CRITERIA_CONDITION_NONE)
|
||||
{
|
||||
ASSERT(criteria->AdditionalRequirements[i].Type < ACHIEVEMENT_CRITERIA_CONDITION_MAX,
|
||||
"ACHIEVEMENT_CRITERIA_CONDITION_MAX must be greater than or equal to %u but is currently equal to %u",
|
||||
criteria->AdditionalRequirements[i].Type + 1, ACHIEVEMENT_CRITERIA_CONDITION_MAX);
|
||||
if (criteria->StartEvent)
|
||||
_criteriasByStartEvent[criteria->StartEvent][criteria->StartAsset].push_back(criteria);
|
||||
|
||||
if (i == 0
|
||||
|| criteria->AdditionalRequirements[i].Type != criteria->AdditionalRequirements[i - 1].Type
|
||||
|| criteria->AdditionalRequirements[i].Asset != criteria->AdditionalRequirements[i - 1].Asset)
|
||||
m_AchievementCriteriasByCondition[criteria->AdditionalRequirements[i].Type][criteria->AdditionalRequirements[i].Asset].push_back(criteria);
|
||||
}
|
||||
// Timed criterias use a own event definition which is name-identical with StartEvent so we translate it and add the criterias to the StartEvent container
|
||||
if (criteria->TimerStartEvent)
|
||||
{
|
||||
AchievementCriteriaStartEvent startEvent = ToStartEvent(criteria->TimerStartEvent);
|
||||
if (startEvent != AchievementCriteriaStartEvent::None)
|
||||
_criteriasByStartEvent[AsUnderlyingType(startEvent)][criteria->TimerAsset].push_back(criteria);
|
||||
}
|
||||
|
||||
if (criteria->StartTimer)
|
||||
{
|
||||
ASSERT(criteria->StartEvent < ACHIEVEMENT_TIMED_TYPE_MAX, "ACHIEVEMENT_TIMED_TYPE_MAX must be greater than or equal to %u but is currently equal to %u",
|
||||
criteria->StartEvent + 1, ACHIEVEMENT_TIMED_TYPE_MAX);
|
||||
m_AchievementCriteriasByTimedType[criteria->StartEvent].push_back(criteria);
|
||||
}
|
||||
if (criteria->FailEvent)
|
||||
_criteriasByFailEvent[criteria->FailEvent][criteria->FailAsset].push_back(criteria);
|
||||
}
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms.", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime));
|
||||
|
||||
@@ -36,7 +36,6 @@ typedef std::vector<AchievementEntry const*> AchievementEntryList;
|
||||
|
||||
typedef std::unordered_map<uint32, AchievementCriteriaEntryList> AchievementCriteriaListByAchievement;
|
||||
typedef std::unordered_map<uint32, AchievementCriteriaEntryList> AchievementCriteriaListByMiscValue;
|
||||
typedef std::unordered_map<uint32, AchievementCriteriaEntryList> AchievementCriteriaListByCondition;
|
||||
typedef std::unordered_map<uint32, AchievementEntryList> AchievementListByReferencedId;
|
||||
|
||||
struct CriteriaProgress
|
||||
@@ -266,7 +265,6 @@ class TC_GAME_API AchievementMgr
|
||||
static void DeleteFromDB(ObjectGuid lowguid);
|
||||
void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult);
|
||||
void SaveToDB(CharacterDatabaseTransaction& trans);
|
||||
void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete);
|
||||
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject const* ref = nullptr, Player* referencePlayer = nullptr, GameObject* go = nullptr);
|
||||
void CompletedAchievement(AchievementEntry const* entry, Player* referencePlayer);
|
||||
void CheckAllAchievementCriteria(Player* referencePlayer);
|
||||
@@ -277,9 +275,9 @@ class TC_GAME_API AchievementMgr
|
||||
CompletedAchievementData* GetCompletedDataForAchievement(uint32 achievementId);
|
||||
T* GetOwner() const { return _owner; }
|
||||
|
||||
void UpdateTimedAchievements(uint32 timeDiff);
|
||||
void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost = 0);
|
||||
void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); // used for quest and scripted timed achievements
|
||||
void UpdateTimedAchievementCriteria(Milliseconds timeDiff);
|
||||
void StartAchievementCriteria(AchievementCriteriaStartEvent startEvent, uint32 asset, Milliseconds timeLost = Milliseconds::zero());
|
||||
void FailAchievementCriteria(AchievementCriteriaFailEvent failEvent, uint32 asset);
|
||||
|
||||
uint32 GetAchievementPoints() const { return _achievementPoints; }
|
||||
private:
|
||||
@@ -301,9 +299,9 @@ class TC_GAME_API AchievementMgr
|
||||
T* _owner;
|
||||
CriteriaProgressMap m_criteriaProgress;
|
||||
CompletedAchievementMap m_completedAchievements;
|
||||
typedef std::map<uint32, uint32> TimedAchievementMap;
|
||||
TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS
|
||||
uint32 _achievementPoints;
|
||||
|
||||
std::unordered_map<uint32 /*criteriaID*/, Milliseconds /*time left*/> _startedCriteria;
|
||||
};
|
||||
|
||||
class TC_GAME_API AchievementGlobalMgr
|
||||
@@ -319,16 +317,9 @@ class TC_GAME_API AchievementGlobalMgr
|
||||
|
||||
AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type, uint32 miscValue, bool guild = false) const;
|
||||
|
||||
AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) const
|
||||
{
|
||||
return m_AchievementCriteriasByTimedType[type];
|
||||
}
|
||||
AchievementCriteriaEntryList const* GetAchievementCriteriaByStartEvent(AchievementCriteriaStartEvent startEvent, int32 asset) const;
|
||||
|
||||
AchievementCriteriaEntryList const* GetAchievementCriteriaByCondition(AchievementCriteriaCondition condition, uint32 val)
|
||||
{
|
||||
AchievementCriteriaListByCondition::const_iterator itr = m_AchievementCriteriasByCondition[condition].find(val);
|
||||
return itr != m_AchievementCriteriasByCondition[condition].end() ? &itr->second : nullptr;
|
||||
}
|
||||
AchievementCriteriaEntryList const* GetAchievementCriteriaByFailEvent(AchievementCriteriaFailEvent startEvent, int32 asset) const;
|
||||
|
||||
AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) const
|
||||
{
|
||||
@@ -399,9 +390,8 @@ class TC_GAME_API AchievementGlobalMgr
|
||||
AchievementCriteriaListByMiscValue m_AchievementCriteriasByMiscValue[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
|
||||
AchievementCriteriaListByMiscValue m_GuildAchievementCriteriasByMiscValue[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
|
||||
|
||||
AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX];
|
||||
|
||||
AchievementCriteriaListByCondition m_AchievementCriteriasByCondition[ACHIEVEMENT_CRITERIA_CONDITION_MAX];
|
||||
std::unordered_map<int32, AchievementCriteriaEntryList> _criteriasByStartEvent[size_t(AchievementCriteriaStartEvent::Count)];
|
||||
std::unordered_map<int32, AchievementCriteriaEntryList> _criteriasByFailEvent[size_t(AchievementCriteriaFailEvent::Count)];
|
||||
|
||||
// store achievement criterias by achievement to speed up lookup
|
||||
AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement;
|
||||
|
||||
@@ -309,6 +309,7 @@ void Arena::EndBattleground(uint32 winner)
|
||||
{
|
||||
// update achievement BEFORE personal rating update
|
||||
uint32 rating = player->GetArenaPersonalRating(winnerArenaTeam->GetSlot());
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::WinRankedArenaMatchWithTeamSize, 0);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, rating ? rating : 1);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA, GetMapId());
|
||||
player->ModifyCurrency(CURRENCY_TYPE_CONQUEST_META_ARENA, sWorld->getIntConfig(CONFIG_CURRENCY_CONQUEST_POINTS_ARENA_REWARD));
|
||||
@@ -335,7 +336,7 @@ void Arena::EndBattleground(uint32 winner)
|
||||
loserArenaTeam->MemberLost(player, winnerMatchmakerRating, loserMatchmakerChange);
|
||||
|
||||
// Arena lost => reset the win_rated_arena having the "no_lose" condition
|
||||
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE, 0);
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::LoseRankedArenaMatchWithTeamSize, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -534,6 +534,10 @@ inline void Battleground::_ProcessJoin(uint32 diff)
|
||||
SetStatus(STATUS_IN_PROGRESS);
|
||||
SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
|
||||
|
||||
for (auto const& [guid, _] : GetPlayers())
|
||||
if (Player* player = ObjectAccessor::GetPlayer(GetBgMap(), guid))
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::StartBattleground, GetBgMap()->GetId());
|
||||
|
||||
// Remove preparation
|
||||
if (isArena())
|
||||
{
|
||||
@@ -1021,7 +1025,7 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen
|
||||
player->SetBGTeam(0);
|
||||
|
||||
// remove all criterias on bg leave
|
||||
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::LeaveBattleground, 0);
|
||||
|
||||
player->RemoveBattlegroundQueueJoinTime(bgTypeId);
|
||||
|
||||
@@ -1141,9 +1145,6 @@ void Battleground::AddPlayer(Player* player)
|
||||
}
|
||||
}
|
||||
|
||||
// reset all map criterias on map enter
|
||||
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
|
||||
|
||||
// setup BG group membership
|
||||
PlayerAddedToBGCheckIfBGIsRunning(player);
|
||||
AddOrSetPlayerToCorrectBgGroup(player, team);
|
||||
|
||||
@@ -567,7 +567,7 @@ void BattlegroundTP::EventPlayerClickedOnFlag(Player* player, GameObject* target
|
||||
UpdateFlagState(HORDE, BG_TP_FLAG_STATE_ON_PLAYER);
|
||||
UpdateWorldState(BG_TP_FLAG_UNK_ALLIANCE, 1);
|
||||
player->CastSpell(player, BG_TP_SPELL_ALLIANCE_FLAG, true);
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, BG_TP_SPELL_ALLIANCE_FLAG_PICKED);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::BeSpellTarget, BG_TP_SPELL_ALLIANCE_FLAG_PICKED);
|
||||
if (_flagState[1] == BG_TP_FLAG_STATE_ON_PLAYER)
|
||||
_bothFlagsKept = true;
|
||||
}
|
||||
@@ -585,7 +585,7 @@ void BattlegroundTP::EventPlayerClickedOnFlag(Player* player, GameObject* target
|
||||
UpdateFlagState(ALLIANCE, BG_TP_FLAG_STATE_ON_PLAYER);
|
||||
UpdateWorldState(BG_TP_FLAG_UNK_HORDE, 1);
|
||||
player->CastSpell(player, BG_TP_SPELL_HORDE_FLAG, true);
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, BG_TP_SPELL_HORDE_FLAG_PICKED);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::BeSpellTarget, BG_TP_SPELL_HORDE_FLAG_PICKED);
|
||||
if (_flagState[0] == BG_TP_FLAG_STATE_ON_PLAYER)
|
||||
_bothFlagsKept = true;
|
||||
}
|
||||
|
||||
@@ -464,7 +464,7 @@ void BattlegroundWS::EventPlayerClickedOnFlag(Player* player, GameObject* target
|
||||
//update world state to show correct flag carrier
|
||||
UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER);
|
||||
player->CastSpell(player, BG_WS_SPELL_SILVERWING_FLAG, true);
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, BG_WS_SPELL_SILVERWING_FLAG_PICKED);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::BeSpellTarget, BG_WS_SPELL_SILVERWING_FLAG_PICKED);
|
||||
if (_flagState[1] == BG_WS_FLAG_STATE_ON_PLAYER)
|
||||
_bothFlagsKept = true;
|
||||
}
|
||||
@@ -481,7 +481,7 @@ void BattlegroundWS::EventPlayerClickedOnFlag(Player* player, GameObject* target
|
||||
//update world state to show correct flag carrier
|
||||
UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER);
|
||||
player->CastSpell(player, BG_WS_SPELL_WARSONG_FLAG, true);
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, BG_WS_SPELL_WARSONG_FLAG_PICKED);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::BeSpellTarget, BG_WS_SPELL_WARSONG_FLAG_PICKED);
|
||||
if (_flagState[0] == BG_WS_FLAG_STATE_ON_PLAYER)
|
||||
_bothFlagsKept = true;
|
||||
}
|
||||
|
||||
@@ -100,26 +100,9 @@ enum AchievementFlags
|
||||
|
||||
enum AchievementCriteriaLimits
|
||||
{
|
||||
MAX_CRITERIA_REQUIREMENTS = 2,
|
||||
MAX_ADDITIONAL_CRITERIA_CONDITIONS = 3
|
||||
};
|
||||
|
||||
enum AchievementCriteriaCondition
|
||||
{
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_NONE = 0,
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH = 1, // reset progress on death
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_UNK2 = 2, // only used in "Complete a daily quest every day for five consecutive days"
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP = 3, // requires you to be on specific map, reset at change
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE = 4, // only used in "Win 10 arenas without losing"
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_UNK5 = 5, // Have spell?
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_UNK8 = 8,
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_NO_SPELL_HIT = 9, // requires the player not to be hit by specific spell
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_NOT_IN_GROUP = 10, // requires the player not to be in group
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_UNK13 = 13, // unk
|
||||
|
||||
ACHIEVEMENT_CRITERIA_CONDITION_MAX
|
||||
};
|
||||
|
||||
enum AchievementCriteriaAdditionalCondition
|
||||
{
|
||||
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_DRUNK_VALUE = 1,
|
||||
@@ -180,17 +163,57 @@ enum AchievementCriteriaFlags
|
||||
ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER = 0x00000020 // Displays counter as money
|
||||
};
|
||||
|
||||
enum AchievementCriteriaTimedTypes : uint8
|
||||
enum class AchievementCriteriaFailEvent : uint8
|
||||
{
|
||||
ACHIEVEMENT_TIMED_TYPE_EVENT = 1, // Timer is started by internal event with id in timerStartEvent
|
||||
ACHIEVEMENT_TIMED_TYPE_QUEST = 2, // Timer is started by accepting 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_UNK = 10, // Unknown
|
||||
None = 0,
|
||||
Death = 1, // Death
|
||||
Hours24WithoutCompletingDailyQuest = 2, // 24 hours without completing a daily quest
|
||||
LeaveBattleground = 3, // Leave a battleground
|
||||
LoseRankedArenaMatchWithTeamSize = 4, // Lose a ranked arena match with team size {#Team Size}
|
||||
LoseAura = 5, // Lose aura "{Spell}"
|
||||
GainAura = 6, // Gain aura "{Spell}"
|
||||
GainAuraEffect = 7, // Gain aura effect "{SpellAuraNames.EnumID}"
|
||||
CastSpell = 8, // Cast spell "{Spell}"
|
||||
BeSpellTarget = 9, // Have spell "{Spell}" cast on you
|
||||
ModifyPartyStatus = 10, // Modify your party status
|
||||
|
||||
ACHIEVEMENT_TIMED_TYPE_MAX
|
||||
Count
|
||||
};
|
||||
|
||||
enum class AchievementCriteriaStartEvent : uint8
|
||||
{
|
||||
None = 0, // - NONE -
|
||||
ReachLevel = 1, // Reach level {#Level}
|
||||
CompleteDailyQuest = 2, // Complete daily quest "{QuestV2}"
|
||||
StartBattleground = 3, // Start battleground "{Map}"
|
||||
WinRankedArenaMatchWithTeamSize = 4, // Win a ranked arena match with team size {#Team Size}
|
||||
GainAura = 5, // Gain aura "{Spell}"
|
||||
GainAuraEffect = 6, // Gain aura effect "{SpellAuraNames.EnumID}"
|
||||
CastSpell = 7, // Cast spell "{Spell}"
|
||||
BeSpellTarget = 8, // Have spell "{Spell}" cast on you
|
||||
AcceptQuest = 9, // Accept quest "{QuestV2}"
|
||||
KillNPC = 10, // Kill NPC "{Creature}"
|
||||
KillPlayer = 11, // Kill player
|
||||
UseItem = 12, // Use item "{Item}"
|
||||
SendEvent = 13, // Send event "{GameEvents}" (player-sent/instance only)
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum class AchievementCriteriaTimerStartEvent : uint8
|
||||
{
|
||||
None = 0,
|
||||
SendEvent = 1,
|
||||
AcceptQuest = 2,
|
||||
Unused3 = 3,
|
||||
Unused4 = 4,
|
||||
CastSpell = 5,
|
||||
GainAura = 6,
|
||||
KillNPC = 7,
|
||||
Unused8 = 8,
|
||||
UseItem = 9,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum AchievementCriteriaTypes : uint8
|
||||
|
||||
@@ -58,7 +58,7 @@ struct AchievementCategoryEntry
|
||||
struct AchievementCriteriaEntry
|
||||
{
|
||||
uint32 ID; // 0
|
||||
uint32 ReferredAchievement; // 1
|
||||
uint32 AchievementID; // 1
|
||||
uint32 Type; // 2
|
||||
|
||||
union
|
||||
@@ -164,25 +164,19 @@ struct AchievementCriteriaEntry
|
||||
|
||||
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE_TYPE = 138
|
||||
uint32 GuildChallengeType;
|
||||
} Asset;
|
||||
|
||||
uint64 Amount;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 Type;
|
||||
uint32 Asset;
|
||||
} AdditionalRequirements[MAX_CRITERIA_REQUIREMENTS];
|
||||
} Asset; // 3
|
||||
|
||||
uint64 Quantity; // 4
|
||||
uint32 StartEvent; // 5
|
||||
int32 StartAsset; // 6
|
||||
uint32 FailEvent; // 7
|
||||
int32 FailAsset; // 8
|
||||
char* Description; // 9
|
||||
uint32 Flags; // 10
|
||||
uint32 StartEvent; // 11 Only appears with timed achievements, seems to be the type of starting a timed Achievement, only type 1 and some of type 6 need manual starting
|
||||
// 1: ByEventId(?) (serverside IDs), 2: ByQuestId, 5: ByCastSpellId(?)
|
||||
// 6: BySpellIdTarget(some of these are unknown spells, some not, some maybe spells)
|
||||
// 7: ByKillNpcId, 9: ByUseItemId
|
||||
uint32 StartAsset; // 12 Alway appears with timed events, used internally to start the achievement, store
|
||||
uint32 StartTimer; // 13 time limit in seconds
|
||||
uint32 OrderIndex; // 14 also used in achievement shift-links as index in state bitmask
|
||||
uint32 Flags; // 10
|
||||
uint32 TimerStartEvent; // 11
|
||||
uint32 TimerAsset; // 12
|
||||
uint32 TimerTime; // 13
|
||||
uint32 OrderIndex; // 14
|
||||
uint32 RequiredWorldStateID; // 15
|
||||
int32 RequiredWorldStateValue; // 16
|
||||
uint32 AdditionalConditionType[MAX_ADDITIONAL_CRITERIA_CONDITIONS]; // 17-19
|
||||
|
||||
@@ -1031,7 +1031,7 @@ void Player::Update(uint32 p_time)
|
||||
}
|
||||
}
|
||||
|
||||
m_achievementMgr->UpdateTimedAchievements(p_time);
|
||||
m_achievementMgr->UpdateTimedAchievementCriteria(Milliseconds(p_time));
|
||||
|
||||
if (HasUnitState(UNIT_STATE_MELEE_ATTACKING) && !HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
@@ -1350,7 +1350,7 @@ void Player::setDeathState(DeathState s)
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
|
||||
|
||||
// reset all death criterias
|
||||
ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH, 0);
|
||||
FailAchievementCriteria(AchievementCriteriaFailEvent::Death, 0);
|
||||
}
|
||||
|
||||
Unit::setDeathState(s);
|
||||
@@ -2340,6 +2340,7 @@ void Player::GiveLevel(uint8 level)
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
StartAchievementCriteria(AchievementCriteriaStartEvent::ReachLevel, level);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
|
||||
|
||||
// Refer-A-Friend
|
||||
@@ -14188,7 +14189,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
|
||||
|
||||
m_QuestStatusSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
|
||||
|
||||
StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
|
||||
StartAchievementCriteria(AchievementCriteriaStartEvent::AcceptQuest, quest_id);
|
||||
|
||||
SendQuestUpdate(quest_id);
|
||||
|
||||
@@ -14409,6 +14410,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
||||
SetDailyQuestStatus(quest_id);
|
||||
if (quest->IsDaily())
|
||||
{
|
||||
StartAchievementCriteria(AchievementCriteriaStartEvent::CompleteDailyQuest, 0);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, quest_id);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY, quest_id);
|
||||
}
|
||||
@@ -15653,7 +15655,7 @@ void Player::KilledMonsterCredit(uint32 entry, ObjectGuid guid /*= ObjectGuid::E
|
||||
real_entry = killed->GetEntry();
|
||||
}
|
||||
|
||||
StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_CREATURE, real_entry); // MUST BE CALLED FIRST
|
||||
StartAchievementCriteria(AchievementCriteriaStartEvent::KillNPC, real_entry); // MUST BE CALLED FIRST
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, real_entry, addkillcount, 0, killed);
|
||||
|
||||
for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||
@@ -15709,6 +15711,7 @@ void Player::KilledMonsterCredit(uint32 entry, ObjectGuid guid /*= ObjectGuid::E
|
||||
|
||||
void Player::KilledPlayerCredit(uint16 count)
|
||||
{
|
||||
StartAchievementCriteria(AchievementCriteriaStartEvent::KillPlayer, 0);
|
||||
for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
|
||||
{
|
||||
uint32 questid = GetQuestSlotQuestId(i);
|
||||
@@ -23357,7 +23360,6 @@ void Player::ResetWeeklyQuestStatus()
|
||||
m_weeklyquests.clear();
|
||||
// DB data deleted in caller
|
||||
m_WeeklyQuestChanged = false;
|
||||
|
||||
}
|
||||
|
||||
void Player::ResetSeasonalQuestStatus(uint16 event_id, time_t eventStartTime)
|
||||
@@ -25319,19 +25321,14 @@ bool Player::HasAchieved(uint32 achievementId) const
|
||||
return m_achievementMgr->HasAchieved(achievementId);
|
||||
}
|
||||
|
||||
void Player::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost/* = 0*/)
|
||||
void Player::StartAchievementCriteria(AchievementCriteriaStartEvent startEvent, uint32 entry, Milliseconds timeLost/* = Milliseconds::zero()*/)
|
||||
{
|
||||
m_achievementMgr->StartTimedAchievement(type, entry, timeLost);
|
||||
m_achievementMgr->StartAchievementCriteria(startEvent, entry, timeLost);
|
||||
}
|
||||
|
||||
void Player::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
void Player::FailAchievementCriteria(AchievementCriteriaFailEvent failEvent, int32 failAsset)
|
||||
{
|
||||
m_achievementMgr->RemoveTimedAchievement(type, entry);
|
||||
}
|
||||
|
||||
void Player::ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete /* = false*/)
|
||||
{
|
||||
m_achievementMgr->ResetAchievementCriteria(condition, value, evenIfCriteriaComplete);
|
||||
m_achievementMgr->FailAchievementCriteria(failEvent, failAsset);
|
||||
}
|
||||
|
||||
void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject* ref /*= nullptr*/, GameObject* go /*= nullptr*/)
|
||||
|
||||
@@ -2324,10 +2324,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
|
||||
uint32 GetAchievementPoints() const;
|
||||
bool HasAchieved(uint32 achievementId) const;
|
||||
void ResetAchievements();
|
||||
void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete = false);
|
||||
void FailAchievementCriteria(AchievementCriteriaFailEvent condition, int32 failAsset);
|
||||
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject* ref = nullptr, GameObject* go = nullptr);
|
||||
void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost = 0);
|
||||
void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry);
|
||||
void StartAchievementCriteria(AchievementCriteriaStartEvent startEvent, uint32 entry, Milliseconds timeLost = Milliseconds::zero());
|
||||
void CompletedAchievement(AchievementEntry const* entry);
|
||||
|
||||
bool HasTitle(uint32 bitIndex) const;
|
||||
|
||||
@@ -3167,8 +3167,14 @@ void Unit::_ApplyAura(AuraApplication* aurApp, uint8 effMask)
|
||||
}
|
||||
|
||||
if (Player* player = ToPlayer())
|
||||
{
|
||||
if (sConditionMgr->IsSpellUsedInSpellClickConditions(aurApp->GetBase()->GetId()))
|
||||
player->UpdateVisibleGameobjectsOrSpellClicks();
|
||||
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::GainAura, aurApp->GetBase()->GetId());
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::GainAura, aurApp->GetBase()->GetId());
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, aurApp->GetBase()->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
// removes aura application from lists and unapplies effects
|
||||
@@ -3246,9 +3252,13 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveFlags removeM
|
||||
aura->HandleAuraSpecificMods(aurApp, caster, false, false);
|
||||
|
||||
if (Player* player = ToPlayer())
|
||||
{
|
||||
if (sConditionMgr->IsSpellUsedInSpellClickConditions(aurApp->GetBase()->GetId()))
|
||||
player->UpdateVisibleGameobjectsOrSpellClicks();
|
||||
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::LoseAura, aurApp->GetBase()->GetId());
|
||||
}
|
||||
|
||||
i = m_appliedAuras.begin();
|
||||
}
|
||||
|
||||
@@ -3309,7 +3319,14 @@ void Unit::_RemoveNoStackAurasDueToAura(Aura* aura)
|
||||
void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
m_modAuras[aurEff->GetAuraType()].push_back(aurEff);
|
||||
if (Player* player = ToPlayer())
|
||||
{
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::GainAuraEffect, aurEff->GetAuraType());
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::GainAuraEffect, aurEff->GetAuraType());
|
||||
}
|
||||
}
|
||||
else
|
||||
m_modAuras[aurEff->GetAuraType()].remove(aurEff);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ void GameEvents::TriggerForPlayer(uint32 gameEventId, Player* source)
|
||||
{
|
||||
Map* map = source->GetMap();
|
||||
if (map->Instanceable())
|
||||
source->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, gameEventId);
|
||||
source->StartAchievementCriteria(AchievementCriteriaStartEvent::SendEvent, gameEventId);
|
||||
}
|
||||
|
||||
void GameEvents::TriggerForMap(uint32 gameEventId, Map* map, WorldObject* source, WorldObject* target)
|
||||
|
||||
@@ -499,6 +499,8 @@ bool Group::AddMember(Player* player)
|
||||
if (isRaidGroup())
|
||||
player->UpdateVisibleGameobjectsOrSpellClicks();
|
||||
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::ModifyPartyStatus, 0);
|
||||
|
||||
{
|
||||
// Broadcast new player group member fields to rest of the group
|
||||
player->SetFieldNotifyFlag(UF_FLAG_PARTY_MEMBER);
|
||||
@@ -579,6 +581,9 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R
|
||||
if (isLFGGroup() && method == GROUP_REMOVEMETHOD_KICK)
|
||||
return !m_memberSlots.empty();
|
||||
|
||||
if (player)
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::ModifyPartyStatus, 0);
|
||||
|
||||
// remove member and change leader (if need) only if strong more 2 members _before_ member remove (BG/BF allow 1 member group)
|
||||
if (GetMembersCount() > ((isBGGroup() || isLFGGroup() || isBFGroup()) ? 1u : 2u))
|
||||
{
|
||||
|
||||
@@ -409,7 +409,6 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemove
|
||||
_player->TakeQuestSourceItem(questId, true); // remove quest src item from player
|
||||
_player->AbandonQuest(questId); // remove all quest items player received before abandoning quest. Note, this does not remove normal drop items that happen to be quest requirements.
|
||||
_player->RemoveActiveQuest(questId);
|
||||
_player->RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, questId);
|
||||
|
||||
TC_LOG_INFO("network", "Player %u abandoned quest %u", _player->GetGUID().GetCounter(), questId);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DBCStores.h"
|
||||
#include "GameEventSender.h"
|
||||
#include "GameObject.h"
|
||||
#include "Group.h"
|
||||
#include "Guild.h"
|
||||
@@ -121,6 +122,25 @@ ObjectGuid InstanceScript::GetGuidData(uint32 type) const
|
||||
return GetObjectGuid(type);
|
||||
}
|
||||
|
||||
|
||||
void InstanceScript::TriggerGameEvent(uint32 gameEventId, WorldObject* source /*= nullptr*/, WorldObject* target /*= nullptr*/)
|
||||
{
|
||||
if (source)
|
||||
{
|
||||
ZoneScript::TriggerGameEvent(gameEventId, source, target);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessEvent(target, gameEventId, source);
|
||||
instance->DoOnPlayers([gameEventId](Player* player)
|
||||
{
|
||||
GameEvents::TriggerForPlayer(gameEventId, player);
|
||||
});
|
||||
|
||||
GameEvents::TriggerForMap(gameEventId, instance);
|
||||
}
|
||||
|
||||
|
||||
Creature* InstanceScript::GetCreature(uint32 type)
|
||||
{
|
||||
return instance->GetCreature(GetObjectGuid(type));
|
||||
@@ -611,28 +631,6 @@ void InstanceScript::DoUpdateAchievementCriteria(AchievementCriteriaTypes type,
|
||||
player->UpdateAchievementCriteria(type, miscValue1, miscValue2, 0, unit);
|
||||
}
|
||||
|
||||
// Start timed achievement for all players in instance
|
||||
void InstanceScript::DoStartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
{
|
||||
Map::PlayerList const& PlayerList = instance->GetPlayers();
|
||||
|
||||
if (!PlayerList.isEmpty())
|
||||
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
|
||||
if (Player* player = i->GetSource())
|
||||
player->StartTimedAchievement(type, entry);
|
||||
}
|
||||
|
||||
// Stop timed achievement for all players in instance
|
||||
void InstanceScript::DoStopTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
|
||||
{
|
||||
Map::PlayerList const& PlayerList = instance->GetPlayers();
|
||||
|
||||
if (!PlayerList.isEmpty())
|
||||
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
|
||||
if (Player* player = i->GetSource())
|
||||
player->RemoveTimedAchievement(type, entry);
|
||||
}
|
||||
|
||||
// Remove Auras due to Spell on all players in instance
|
||||
void InstanceScript::DoRemoveAurasDueToSpellOnPlayers(uint32 spell)
|
||||
{
|
||||
|
||||
@@ -40,8 +40,8 @@ class Player;
|
||||
class Unit;
|
||||
class WorldPacket;
|
||||
enum AchievementCriteriaTypes : uint8;
|
||||
enum AchievementCriteriaTimedTypes : uint8;
|
||||
enum EncounterCreditType : uint8;
|
||||
enum class AchievementCriteriaStartEvent : uint8;
|
||||
|
||||
enum EncounterFrameType
|
||||
{
|
||||
@@ -187,6 +187,10 @@ class TC_GAME_API InstanceScript : public ZoneScript
|
||||
ObjectGuid GetObjectGuid(uint32 type) const;
|
||||
virtual ObjectGuid GetGuidData(uint32 type) const override;
|
||||
|
||||
// Triggers a GameEvent
|
||||
// * If source is nullptr then event is triggered for each player in the instance as "source"
|
||||
void TriggerGameEvent(uint32 gameEventId, WorldObject* source = nullptr, WorldObject* target = nullptr) override;
|
||||
|
||||
Creature* GetCreature(uint32 type);
|
||||
GameObject* GetGameObject(uint32 type);
|
||||
|
||||
@@ -214,10 +218,6 @@ class TC_GAME_API InstanceScript : public ZoneScript
|
||||
// Update Achievement Criteria for all players in instance
|
||||
void DoUpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = nullptr);
|
||||
|
||||
// Start/Stop Timed Achievement Criteria for all players in instance
|
||||
void DoStartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry);
|
||||
void DoStopTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry);
|
||||
|
||||
// Remove Auras due to Spell on all players in instance
|
||||
void DoRemoveAurasDueToSpellOnPlayers(uint32 spell);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "GridDefines.h"
|
||||
#include "GridRefManager.h"
|
||||
#include "MapDefines.h"
|
||||
#include "MapReference.h"
|
||||
#include "MapRefManager.h"
|
||||
#include "MPSCQueue.h"
|
||||
#include "ObjectGuid.h"
|
||||
@@ -335,6 +336,14 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
|
||||
typedef MapRefManager PlayerList;
|
||||
PlayerList const& GetPlayers() const { return m_mapRefManager; }
|
||||
|
||||
template <typename T>
|
||||
void DoOnPlayers(T&& fn)
|
||||
{
|
||||
for (MapReference const& ref : GetPlayers())
|
||||
if (Player* player = ref.GetSource())
|
||||
fn(player);
|
||||
}
|
||||
|
||||
//per-map script storage
|
||||
void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo>> const& scripts, uint32 id, Object* source, Object* target);
|
||||
void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target);
|
||||
|
||||
@@ -17,8 +17,25 @@
|
||||
|
||||
#include "ZoneScript.h"
|
||||
#include "Creature.h"
|
||||
#include "GameEventSender.h"
|
||||
|
||||
ZoneScript::ZoneScript()
|
||||
{
|
||||
}
|
||||
|
||||
ZoneScript::~ZoneScript()
|
||||
{
|
||||
}
|
||||
|
||||
uint32 ZoneScript::GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data)
|
||||
{
|
||||
return data->id;
|
||||
}
|
||||
|
||||
void ZoneScript::TriggerGameEvent(uint32 gameEventId, WorldObject* source /*= nullptr*/, WorldObject* target /*= nullptr*/)
|
||||
{
|
||||
if (source)
|
||||
GameEvents::Trigger(gameEventId, source, target);
|
||||
else
|
||||
ProcessEvent(nullptr, gameEventId, nullptr);
|
||||
}
|
||||
@@ -30,8 +30,8 @@ struct CreatureData;
|
||||
class TC_GAME_API ZoneScript
|
||||
{
|
||||
public:
|
||||
ZoneScript() { }
|
||||
virtual ~ZoneScript() { }
|
||||
ZoneScript();
|
||||
virtual ~ZoneScript();
|
||||
|
||||
virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data);
|
||||
virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) { return entry; }
|
||||
@@ -55,6 +55,7 @@ class TC_GAME_API ZoneScript
|
||||
virtual uint32 GetData(uint32 /*DataId*/) const { return 0; }
|
||||
virtual void SetData(uint32 /*DataId*/, uint32 /*Value*/) { }
|
||||
|
||||
virtual void TriggerGameEvent(uint32 gameEventId, WorldObject* source = nullptr, WorldObject* target = nullptr);
|
||||
virtual void ProcessEvent(WorldObject* /*obj*/, uint32 /*eventId*/, WorldObject* /*invoker*/) { }
|
||||
};
|
||||
|
||||
|
||||
@@ -2930,16 +2930,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo&
|
||||
|
||||
if (Player* player = unit->ToPlayer())
|
||||
{
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, m_spellInfo->Id);
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::BeSpellTarget, m_spellInfo->Id);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::BeSpellTarget, m_spellInfo->Id);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id, 0, 0, m_caster);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id);
|
||||
}
|
||||
|
||||
if (Player* player = m_caster->ToPlayer())
|
||||
{
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_CASTER, m_spellInfo->Id);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, 0, unit);
|
||||
}
|
||||
|
||||
if (m_caster != unit)
|
||||
{
|
||||
@@ -3669,10 +3666,12 @@ void Spell::_cast(bool skipCheck)
|
||||
{
|
||||
if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_ITEM) && m_CastItem)
|
||||
{
|
||||
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_ITEM, m_CastItem->GetEntry());
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::UseItem, m_CastItem->GetEntry());
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry());
|
||||
}
|
||||
|
||||
player->FailAchievementCriteria(AchievementCriteriaFailEvent::CastSpell, m_spellInfo->Id);
|
||||
player->StartAchievementCriteria(AchievementCriteriaStartEvent::CastSpell, m_spellInfo->Id);
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id);
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ class instance_halls_of_origination : public InstanceMapScript
|
||||
if (GameObject* door = GetGameObject(DATA_VAULT_OF_LIGHTS_ENTRANCE_DOOR))
|
||||
door->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_VAULT_OF_LIGHTS_START_EVENT);
|
||||
TriggerGameEvent(ACHIEV_VAULT_OF_LIGHTS_START_EVENT);
|
||||
_events.ScheduleEvent(EVENT_FAIL_VAULTS_OF_LIGHT_ACHIEVEMENT, 5min);
|
||||
}
|
||||
else if (value == DONE)
|
||||
|
||||
@@ -157,7 +157,6 @@ public:
|
||||
_Reset();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
instance->SetData(DATA_ONYXIA_PHASE, Phase);
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
@@ -168,7 +167,7 @@ public:
|
||||
events.ScheduleEvent(EVENT_TAIL_SWEEP, urand(15000, 20000));
|
||||
events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 5000));
|
||||
events.ScheduleEvent(EVENT_WING_BUFFET, urand(10000, 20000));
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMED_START_EVENT);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summoned) override
|
||||
|
||||
@@ -171,7 +171,6 @@ public:
|
||||
Initialize();
|
||||
|
||||
instance->SetBossState(DATA_HERALD_VOLAZJ, NOT_STARTED);
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT);
|
||||
|
||||
// Visible for all players in insanity
|
||||
for (uint32 i = 173; i <= 177; ++i)
|
||||
@@ -191,7 +190,7 @@ public:
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
instance->SetBossState(DATA_HERALD_VOLAZJ, IN_PROGRESS);
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_QUICK_DEMISE_START_EVENT);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
|
||||
@@ -122,7 +122,6 @@ public:
|
||||
{
|
||||
BossAI::Reset();
|
||||
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT);
|
||||
_nextSubmerge = 75;
|
||||
_petCount = 0;
|
||||
}
|
||||
@@ -139,7 +138,7 @@ public:
|
||||
door2->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
Talk(SAY_AGGRO);
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_GOTTA_GO_START_EVENT);
|
||||
|
||||
events.SetPhase(PHASE_EMERGE);
|
||||
events.ScheduleEvent(EVENT_CLOSE_DOOR, Seconds(5));
|
||||
|
||||
+1
-3
@@ -370,8 +370,6 @@ class boss_fjola : public CreatureScript
|
||||
TwinPactSpellId = SPELL_LIGHT_TWIN_PACT;
|
||||
TouchSpellId = SPELL_LIGHT_TOUCH;
|
||||
SpikeSpellId = SPELL_LIGHT_TWIN_SPIKE;
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, EVENT_START_TWINS_FIGHT);
|
||||
boss_twin_baseAI::Reset();
|
||||
}
|
||||
|
||||
@@ -410,7 +408,7 @@ class boss_fjola : public CreatureScript
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, EVENT_START_TWINS_FIGHT);
|
||||
instance->TriggerGameEvent(EVENT_START_TWINS_FIGHT);
|
||||
events.ScheduleEvent(EVENT_SPECIAL_ABILITY, 45 * IN_MILLISECONDS);
|
||||
me->SummonCreature(NPC_BULLET_CONTROLLER, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 0.0f, TEMPSUMMON_MANUAL_DESPAWN);
|
||||
boss_twin_baseAI::JustEngagedWith(who);
|
||||
|
||||
@@ -812,7 +812,6 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript
|
||||
_events.Reset();
|
||||
_icewall = 0;
|
||||
_events.ScheduleEvent(EVENT_ESCAPE, 1000);
|
||||
_instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_NOT_RETREATING_EVENT);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
@@ -1062,7 +1061,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript
|
||||
}
|
||||
}
|
||||
_invincibility = false;
|
||||
_instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_NOT_RETREATING_EVENT);
|
||||
_instance->TriggerGameEvent(ACHIEV_NOT_RETREATING_EVENT);
|
||||
_events.ScheduleEvent(EVENT_ESCAPE_7, 1000);
|
||||
break;
|
||||
case EVENT_ESCAPE_7:
|
||||
|
||||
-2
@@ -348,8 +348,6 @@ class instance_halls_of_reflection : public InstanceMapScript
|
||||
}
|
||||
break;
|
||||
case FAIL:
|
||||
DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_NOT_RETREATING_EVENT);
|
||||
|
||||
if (Creature* jainaOrSylvanas = instance->GetCreature(JainaOrSylvanasEscapeGUID))
|
||||
jainaOrSylvanas->DespawnOrUnsummon(10000);
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
_JustDied();
|
||||
|
||||
// start achievement timer (kill Maexna within 20 min)
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMED_START_EVENT);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
|
||||
@@ -72,8 +72,6 @@ public:
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*Victim*/) override
|
||||
@@ -96,7 +94,7 @@ public:
|
||||
events.ScheduleEvent(EVENT_HATEFUL, Seconds(1));
|
||||
events.ScheduleEvent(EVENT_BERSERK, Minutes(6));
|
||||
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
|
||||
@@ -381,7 +381,6 @@ public:
|
||||
|
||||
SetPhase(PHASE_NOT_STARTED, true);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->SetBossState(DATA_MALYGOS_EVENT, NOT_STARTED);
|
||||
}
|
||||
|
||||
@@ -573,7 +572,7 @@ public:
|
||||
|
||||
Talk(SAY_START_P_ONE);
|
||||
DoCast(SPELL_BERSERK); // periodic aura, first tick in 10 minutes
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMED_START_EVENT);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
|
||||
@@ -138,7 +138,7 @@ class boss_drakos : public CreatureScript
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
// start achievement timer (kill Eregos within 20 min)
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMED_START_EVENT);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
|
||||
@@ -82,7 +82,7 @@ struct boss_loken : public BossAI
|
||||
events.ScheduleEvent(EVENT_ARC_LIGHTNING, 15s);
|
||||
events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 20s);
|
||||
events.ScheduleEvent(EVENT_RESUME_PULSING_SHOCKWAVE, 1s);
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMELY_DEATH_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMELY_DEATH_START_EVENT);
|
||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ struct boss_loken : public BossAI
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
{
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMELY_DEATH_START_EVENT);
|
||||
_DespawnAtEvade();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,6 @@ class boss_maiden_of_grief : public CreatureScript
|
||||
events.ScheduleEvent(EVENT_STORM_OF_GRIEF, 10000);
|
||||
events.ScheduleEvent(EVENT_SHOCK_OF_SORROW, urand(20000, 25000));
|
||||
events.ScheduleEvent(EVENT_PILLAR_OF_WOE, urand(5000, 15000));
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOOD_GRIEF_START_EVENT);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
@@ -76,7 +74,7 @@ class boss_maiden_of_grief : public CreatureScript
|
||||
BossAI::JustEngagedWith(who);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOOD_GRIEF_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_GOOD_GRIEF_START_EVENT);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
|
||||
@@ -787,7 +787,7 @@ class npc_living_constellation : public CreatureScript
|
||||
|
||||
me->DespawnOrUnsummon(1);
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, EVENT_ID_SUPERMASSIVE_START);
|
||||
instance->TriggerGameEvent(EVENT_ID_SUPERMASSIVE_START);
|
||||
caster->CastSpell((Unit*)nullptr, SPELL_BLACK_HOLE_CREDIT, TRIGGERED_FULL_MASK);
|
||||
caster->ToCreature()->DespawnOrUnsummon(1);
|
||||
}
|
||||
@@ -1329,33 +1329,6 @@ class spell_algalon_cosmic_smash_damage : public SpellScriptLoader
|
||||
}
|
||||
};
|
||||
|
||||
class spell_algalon_supermassive_fail : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
spell_algalon_supermassive_fail() : SpellScriptLoader("spell_algalon_supermassive_fail") { }
|
||||
|
||||
class spell_algalon_supermassive_fail_SpellScript : public SpellScript
|
||||
{
|
||||
void RecalculateDamage()
|
||||
{
|
||||
if (!GetHitPlayer())
|
||||
return;
|
||||
|
||||
GetHitPlayer()->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_SPELL_HIT, GetSpellInfo()->Id, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnHit.Register(&spell_algalon_supermassive_fail_SpellScript::RecalculateDamage);
|
||||
}
|
||||
};
|
||||
|
||||
SpellScript* GetSpellScript() const override
|
||||
{
|
||||
return new spell_algalon_supermassive_fail_SpellScript();
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_algalon_the_observer()
|
||||
{
|
||||
new boss_algalon_the_observer();
|
||||
@@ -1371,5 +1344,4 @@ void AddSC_boss_algalon_the_observer()
|
||||
new spell_algalon_remove_phase();
|
||||
new spell_algalon_cosmic_smash();
|
||||
new spell_algalon_cosmic_smash_damage();
|
||||
new spell_algalon_supermassive_fail();
|
||||
}
|
||||
@@ -137,8 +137,6 @@ class boss_ignis : public CreatureScript
|
||||
_Reset();
|
||||
if (Vehicle* _vehicle = me->GetVehicleKit())
|
||||
_vehicle->RemoveAllPassengers();
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEVEMENT_IGNIS_START_EVENT);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
@@ -152,7 +150,7 @@ class boss_ignis : public CreatureScript
|
||||
events.ScheduleEvent(EVENT_END_POT, 40000);
|
||||
events.ScheduleEvent(EVENT_BERSERK, 480000);
|
||||
Initialize();
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEVEMENT_IGNIS_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEVEMENT_IGNIS_START_EVENT);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
|
||||
@@ -200,7 +200,7 @@ class boss_kologarn : public CreatureScript
|
||||
if (!right && !left)
|
||||
events.ScheduleEvent(EVENT_STONE_SHOUT, 5000);
|
||||
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_DISARMED);
|
||||
instance->TriggerGameEvent(CRITERIA_DISARMED);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -203,8 +203,6 @@ class boss_xt002 : public CreatureScript
|
||||
DoCastSelf(SPELL_STAND);
|
||||
|
||||
Initialize();
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MUST_DECONSTRUCT_FASTER);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
@@ -224,7 +222,7 @@ class boss_xt002 : public CreatureScript
|
||||
//Tantrum is cast a bit slower the first time.
|
||||
events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX) * 2);
|
||||
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MUST_DECONSTRUCT_FASTER);
|
||||
instance->TriggerGameEvent(ACHIEV_MUST_DECONSTRUCT_FASTER);
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
|
||||
@@ -493,7 +493,6 @@ class boss_voice_of_yogg_saron : public CreatureScript
|
||||
events.SetPhase(PHASE_ONE);
|
||||
|
||||
instance->SetData(DATA_DRIVE_ME_CRAZY, uint32(true));
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
|
||||
Initialize();
|
||||
|
||||
@@ -517,7 +516,7 @@ class boss_voice_of_yogg_saron : public CreatureScript
|
||||
if (Creature* keeper = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i)))
|
||||
keeper->SetInCombatWith(me);
|
||||
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
instance->TriggerGameEvent(ACHIEV_TIMED_START_EVENT);
|
||||
|
||||
DoCastAOE(SPELL_SUMMON_GUARDIAN_2, { SPELLVALUE_MAX_TARGETS, 1 });
|
||||
DoCast(me, SPELL_SANITY_PERIODIC);
|
||||
|
||||
@@ -558,7 +558,7 @@ class instance_ulduar : public InstanceMapScript
|
||||
case NPC_GUARDIAN_OF_LIFE:
|
||||
if (!conSpeedAtory)
|
||||
{
|
||||
DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_CON_SPEED_ATORY);
|
||||
TriggerGameEvent(CRITERIA_CON_SPEED_ATORY);
|
||||
conSpeedAtory = true;
|
||||
}
|
||||
break;
|
||||
@@ -567,7 +567,7 @@ class instance_ulduar : public InstanceMapScript
|
||||
case NPC_BRIGHTLEAF:
|
||||
if (!lumberjacked)
|
||||
{
|
||||
DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_LUMBERJACKED);
|
||||
TriggerGameEvent(CRITERIA_LUMBERJACKED);
|
||||
lumberjacked = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -169,8 +169,6 @@ public:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
if (!instance->GetCreature(DATA_GRAUF))
|
||||
me->SummonCreature(NPC_GRAUF, GraufLoc);
|
||||
|
||||
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_LODI_DODI_WE_LOVES_THE_SKADI);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
@@ -234,7 +232,7 @@ public:
|
||||
SpawnFirstWave();
|
||||
Talk(SAY_AGGRO);
|
||||
_phase = PHASE_FLYING;
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_LODI_DODI_WE_LOVES_THE_SKADI);
|
||||
instance->TriggerGameEvent(ACHIEV_LODI_DODI_WE_LOVES_THE_SKADI);
|
||||
|
||||
scheduler
|
||||
.Schedule(Seconds(6), [this](TaskContext resetCheck)
|
||||
|
||||
Reference in new issue
Block a user