Jump to content

[10352][patch] Implement function StartTimedAchievement


Schmoozerd

Recommended Posts

What bug does the patch fix? What features does the patch add?

Implements function to start timed achievements

For which repository revision was the patch created?

10305, 10346

Is there a thread in the bug report section or at lighthouse? If yes, please add a link to the thread.

Some regarding single Achievements

Who has been writing this patch? Please include either forum user names or email addresses.

me, Schmoozerd

Description of the new feature

This function is used to Start the timer of an achievement in cases, where there is no relation between the criteria when an achievement is finished and when its timer should start.

Patch:

http://paste2.org/p/947434

Example:

http://www.wowhead.com/achievement=1275

"Bombs Away" Complete the Fires Over Skettis quest in under 2 minutes 15 seconds while not in a group.

This Achievement has Criteria = ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, and hence the timer needs to be started manually on quest _accept_.

There are many Boss Encounter Timed Achievements, but before we can think of the best hook for SD2 we need to get these basics straight.

Other hooks for timed Events (Capture and return flag in time, ...) should be similar to the quest-accept

Remark:

This change, mainly

    if(criteria->timeLimit)
    {
        time_t now = time(NULL);
-        if(time_t(progress->date + criteria->timeLimit) < now)
-            progress->counter = 1;
-
-        // also it seems illogical, the timeframe will be extended at every criteria update
-        progress->date = now;
+        if (time_t(progress->date + criteria->timeLimit) < now)
+        {
+            // Do not reset timer for requirements that are startet manually, also reset their counter, same check as above
+            if (criteria->raw.field3 != criteria->timedCriteriaMiscId || criteria->requiredType == ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST)
+                newValue = 0;
+            else
+            {
+                newValue = 1;
+                // This is used as start time of the achievement, and hence only updated on fail
+                progress->date = now;
+            }
+        }
    }

will result in the following behaviour for _all_ timed achievements:

Achievements can only be completed in timeframe (as it should be)

but the visual timer on client side is updated on every count change, because of OpCodes.

So, this patch fixes all the timed achievements internally, but the client side cannot use this (yet).

An _idea_ for better Opcodes is: (however here is more research needed, and I don't know of these things)

diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp
index b0bb845..db5ab41 100644
--- a/src/game/AchievementMgr.cpp
+++ b/src/game/AchievementMgr.cpp
@@ -655,6 +655,7 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
{
    WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
+    time_t now = time(NULL);
    data << uint32(id);

    // the counter is packed like a packed Guid
@@ -662,8 +663,10 @@ void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progr

    data << GetPlayer()->GetPackGUID();
    data << uint32(0);
-    data << uint32(secsToTimeBitFields(progress->date));
-    data << uint32(0);  // timer 1
+    data << uint32(secsToTimeBitFields(now));               // Used to store time of Criteria Updates?
+                                                            // if not now, then (for normal achievements) dependend on player doing first criteria count, hence 'random'
+    data << uint32(now - progress->date);                   // if 0, then resets the timer for timed Achievements, needs more research,
+                                                            // but syncing server remaining time for timed achievement makes sense
    data << uint32(0);  // timer 2
    GetPlayer()->SendDirectMessage(&data);
}

Edit history:

http://paste2.org/p/935564 Initial version

http://paste2.org/p/936148 Fix problem if player reloggs while on Manual Started Timer

http://paste2.org/p/947434 for Rev 10346

Link to comment
Share on other sites

  • 2 weeks later...
  • 6 months later...

is there a possibility to hook timed achievements in ScriptedInstance class?

im looking towards implementing achievements like... this but i find a limitation as i cannot access Achievement manager to start the timer... (i know its simple to add a timer and make it run, but whats the point if either way you cannot update the achievement progress)

the idea is make a call to ScriptedInstance::OnCreatureEnterCombat (this is only for this kind of achievements - not sure if there's a generic way to make this work) (if creature->entry == XXX) then start timed achievement for all players in the map (?), i'd be thankful of any ideas which could guide me towards the correct solution.

Link to comment
Share on other sites

i read your commit and came up with a question...

what does

progress->counter

mean or do?

with current code im not so sure bout real functionality with timed achievements which require to do X things in Y time

this part:

       CriteriaProgress* progress = NULL;

       CriteriaProgressMap::iterator iter = m_criteriaProgress.find(achievementCriteria->ID);
       if (iter == m_criteriaProgress.end())
           progress = &m_criteriaProgress[achievementCriteria->ID];
       else
           progress = &iter->second;

       progress->changed = true;
       progress->counter = 0;

       // 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);

why do we upate progress->date on every call? say we have to do X thing Y times then b4 sending to client you're telling the client: "achievement just started" on every call and counter is always 0 (idk if counter refers to achievement progress like a bar which counts how many X things have you achieved in Y time)

       CriteriaProgress* progress = NULL;

       CriteriaProgressMap::iterator iter = m_criteriaProgress.find(achievementCriteria->ID);
       if (iter == m_criteriaProgress.end())
       {
           progress = &m_criteriaProgress[achievementCriteria->ID];
           progress->changed = true;
           progress->counter = 0;

           // 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);
       }
       else
           progress = &iter->second;

       SendCriteriaUpdate(achievementCriteria->ID, progress);

thats my idea, correct me if im mistaken or something if i missed anything

Link to comment
Share on other sites

i did test, and changed a couple of things, progress is updated correctly at client with my changes (grouped timed achievements by timedtype)

and added a "start" call b4 calling update... start will be there only to create an entry in m_criteriaFailTimes added more checks to prevent updating achievements which are timed and dont have entries (via scripting hacks or something)

Link to comment
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use