aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/AchievementMgr.cpp135
-rw-r--r--src/game/AchievementMgr.h11
-rw-r--r--src/game/DBCEnums.h14
-rw-r--r--src/game/DBCStructure.h4
-rw-r--r--src/game/DBCfmt.h2
-rw-r--r--src/game/Player.cpp6
-rw-r--r--src/game/QuestHandler.cpp1
-rw-r--r--src/game/Spell.cpp5
8 files changed, 152 insertions, 26 deletions
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp
index 91567587231..cb72e2b9848 100644
--- a/src/game/AchievementMgr.cpp
+++ b/src/game/AchievementMgr.cpp
@@ -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());
- data << uint32(0);
+ 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);
+
+ // Remove the timer, we wont need it anymore
+ if (timedCompleted)
+ m_timedAchievements.erase(timedIter);
+ }
- // also it seems illogical, the timeframe will be extended at every criteria update
- progress->date = now;
+ 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;
diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h
index 1e810a5160d..ea9e5e95142 100644
--- a/src/game/AchievementMgr.h
+++ b/src/game/AchievementMgr.h
@@ -249,12 +249,15 @@ class AchievementMgr
void SendAllAchievementData();
void SendRespondInspectAchievements(Player* player);
bool HasAchieved(AchievementEntry const* achievement) const;
- Player* GetPlayer() { return m_player;}
+ 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
diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h
index 346aa886c44..3969578cd02 100644
--- a/src/game/DBCEnums.h
+++ b/src/game/DBCEnums.h
@@ -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
diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h
index 35fe22242b2..77393f2c4b4 100644
--- a/src/game/DBCStructure.h
+++ b/src/game/DBCStructure.h
@@ -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
diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h
index b2507bd1ff1..e192f3594b5 100644
--- a/src/game/DBCfmt.h
+++ b/src/game/DBCfmt.h
@@ -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";
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 540c3c6b1aa..2715ea91a26 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -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);
diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp
index 1da286509dd..9b03f68efa8 100644
--- a/src/game/QuestHandler.cpp
+++ b/src/game/QuestHandler.cpp
@@ -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);
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index cea7d10735c..fb871a789e9 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -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);
}