From ef5ea6f24f72bd7fdb28727e8895b16b7481d129 Mon Sep 17 00:00:00 2001 From: Schmoozerd Date: Mon, 1 Nov 2010 05:44:32 +0300 Subject: [PATCH] [10667] Timed Achievements, Fail-Support and Opcodes Signed-off-by: VladimirMangos --- src/game/AchievementMgr.cpp | 132 ++++++++++++++++++++++++++---------- src/game/AchievementMgr.h | 4 ++ src/game/Player.cpp | 3 + src/shared/revision_nr.h | 2 +- 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index 4c3387af9..ec836c58c 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -523,8 +523,15 @@ void AchievementMgr::SaveToDB() ssdel << iter->first; } - // store data only for real progress - if(iter->second.counter != 0) + // store data only for real progress, exceptions are timedCriterias, they are also stored for counter == 0 + bool needSave = iter->second.counter != 0; + if (!needSave) + { + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(iter->first); + needSave = criteria && criteria->timeLimit > 0; + } + + if (needSave) { /// first new/changed record prefix if(!need_execute_ins) @@ -597,13 +604,27 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri continue; } - if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL)) - continue; - CriteriaProgress& progress = m_criteriaProgress[id]; progress.counter = counter; progress.date = date; progress.changed = false; + progress.timedCriteriaFailed = false; + + // A failed achievement will be removed on next tick - TODO: Possible that timer 2 is reseted + if (criteria->timeLimit) + { + AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); + + // Add not-completed achievements to time map + if (!IsCompletedCriteria(criteria, achievement)) + { + time_t failTime = time_t(progress.date + criteria->timeLimit); + m_criteriaFailTimes[criteria->ID] = failTime; + // A failed Achievement - will be removed by DoFailedTimedAchievementCriterias on next tick for player + if (failTime <= time(NULL)) + progress.timedCriteriaFailed = true; + } + } // check intergiry with max allowed counter value if (uint32 maxcounter = GetCriteriaProgressMaxCounter(criteria)) @@ -667,14 +688,15 @@ void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progr WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); data << uint32(id); + time_t now = time(NULL); // the counter is packed like a packed Guid data.appendPackGUID(progress->counter); data << GetPlayer()->GetPackGUID(); - data << uint32(0); - data << uint32(secsToTimeBitFields(progress->date)); - data << uint32(0); // timer 1 - data << uint32(0); // timer 2 + data << uint32(progress->timedCriteriaFailed ? 1 : 0); + data << uint32(secsToTimeBitFields(now)); + data << uint32(now - progress->date); // timer 1 + data << uint32(now - progress->date); // timer 2 GetPlayer()->SendDirectMessage(&data); } @@ -758,11 +780,58 @@ void AchievementMgr::StartTimedAchievementCriteria(AchievementCriteriaTypes type // Start with given startTime or now progress->date = startTime ? startTime : time(NULL); + progress->timedCriteriaFailed = false; + + // Add to timer map + m_criteriaFailTimes[achievementCriteria->ID] = time_t(progress->date + achievementCriteria->timeLimit); SendCriteriaUpdate(achievementCriteria->ID, progress); } } +/** + * this function will be called whenever there could be a timed achievement criteria failed because of ellapsed time + */ +void AchievementMgr::DoFailedTimedAchievementCriterias() +{ + if (m_criteriaFailTimes.empty()) + return; + + time_t now = time(NULL); + for (AchievementCriteriaFailTimeMap::iterator iter = m_criteriaFailTimes.begin(); iter != m_criteriaFailTimes.end();) + { + if (iter->second > now) + { + ++iter; + continue; + } + + // Possible failed achievement criteria found + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(iter->first); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); + + // Send Fail for failed criterias + if (!IsCompletedCriteria(criteria, achievement)) + { + DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::DoFailedTimedAchievementCriterias for criteria %u", criteria->ID); + + CriteriaProgressMap::iterator pro_iter = m_criteriaProgress.find(criteria->ID); + MANGOS_ASSERT(pro_iter != m_criteriaProgress.end()); + + CriteriaProgress* progress = &pro_iter->second; + + // Set to failed, and send to client + progress->timedCriteriaFailed = true; + SendCriteriaUpdate(criteria->ID, progress); + + // Remove failed progress + m_criteriaProgress.erase(pro_iter); + } + + iter = m_criteriaFailTimes.erase(iter); + } +} + /** * this function will be called whenever the user might have done a criteria relevant action */ @@ -1902,13 +1971,24 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri if(changeValue == 0) return; - // not start manually started timed achievements - must be same check as below (ie possible on player logout between start and finish) + // not start manually started timed achievements if (criteria->IsExplicitlyStartedTimedCriteria()) return; progress = &m_criteriaProgress[criteria->ID]; - newValue = changeValue; + progress->date = time(NULL); + progress->timedCriteriaFailed = false; + + // timed criterias are added to fail-timer map, and send the starting with counter=0 + if (criteria->timeLimit) + { + m_criteriaFailTimes[criteria->ID] = time_t(progress->date + criteria->timeLimit); + progress->counter = 0; + SendCriteriaUpdate(criteria->ID, progress); + } + + newValue = changeValue; } else { @@ -1936,27 +2016,6 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri return; } - progress->changed = true; - - if(criteria->timeLimit) - { - time_t now = time(NULL); - - // too later, need restart from 1 auto-started or just fail explcit started - if (time_t(progress->date + criteria->timeLimit) < now) - { - // Do not reset timer for requirements that are started manually, also reset their counter, same check as above - if (criteria->IsExplicitlyStartedTimedCriteria()) - newValue = 0; - else - { - newValue = 1; - // This is used as start time of the achievement, and hence only updated on fail - progress->date = now; - } - } - } - progress->counter = newValue; progress->changed = true; @@ -2144,15 +2203,16 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket *data) } *data << int32(-1); + time_t now = time(NULL); for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) { *data << uint32(iter->first); data->appendPackGUID(iter->second.counter); *data << GetPlayer()->GetPackGUID(); - *data << uint32(0); - *data << uint32(secsToTimeBitFields(iter->second.date)); - *data << uint32(0); - *data << uint32(0); + *data << uint32(iter->second.timedCriteriaFailed ? 1 : 0); + *data << uint32(secsToTimeBitFields(now)); + *data << uint32(now - iter->second.date); + *data << uint32(now - iter->second.date); } *data << int32(-1); diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h index 3a44c03e0..b9e1334c8 100644 --- a/src/game/AchievementMgr.h +++ b/src/game/AchievementMgr.h @@ -33,12 +33,14 @@ typedef std::list AchievementEntryList; typedef std::map AchievementCriteriaListByAchievement; typedef std::map AchievementListByReferencedId; +typedef std::map AchievementCriteriaFailTimeMap; struct CriteriaProgress { uint32 counter; time_t date; bool changed; + bool timedCriteriaFailed; }; enum AchievementCriteriaRequirementType @@ -250,6 +252,7 @@ class AchievementMgr void SaveToDB(); void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0); void StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime = 0); + void DoFailedTimedAchievementCriterias(); void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); void CheckAllAchievementCriteria(); void SendAllAchievementData(); @@ -292,6 +295,7 @@ class AchievementMgr Player* m_player; CriteriaProgressMap m_criteriaProgress; CompletedAchievementMap m_completedAchievements; + AchievementCriteriaFailTimeMap m_criteriaFailTimes; }; class AchievementGlobalMgr diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 96a5d47a3..9ed0fee33 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -1180,6 +1180,9 @@ void Player::Update( uint32 p_time ) if(!IsInWorld()) return; + // remove failed timed Achievements + GetAchievementMgr().DoFailedTimedAchievementCriterias(); + // undelivered mail if(m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL)) { diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index af75a63f8..e95081c16 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10666" + #define REVISION_NR "10667" #endif // __REVISION_NR_H__