[10352] Implement support explcitly started timed achievements.

* Player::StartTimedAchievementCriteria can be used for activate timer
  for specific explicitly starting timed achievement by timedRequirementId
  in script.

* for ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST this done in core.

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
This commit is contained in:
Schmoozerd 2010-08-12 23:10:55 +04:00 committed by VladimirMangos
parent 64fe2cf3ed
commit 12aa1bc606
8 changed files with 113 additions and 19 deletions

View file

@ -703,6 +703,70 @@ static const uint32 achievIdForDangeon[][4] =
static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
/**
* this function will be called whenever the user might have done a timed-criteria relevant action, or by scripting side?
*/
void AchievementMgr::StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime /*= 0*/)
{
DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::StartTimedAchievementCriteria(%u, %u)", type, timedRequirementId);
if (!sWorld.getConfig(CONFIG_BOOL_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
return;
AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
{
AchievementCriteriaEntry const *achievementCriteria = (*i);
// only apply to specific timedRequirementId related criteria
if (achievementCriteria->timedCriteriaMiscId != timedRequirementId)
continue;
if (!achievementCriteria->IsExplicitlyStartedTimedCriteria())
continue;
if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
continue;
AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
if (!achievement)
continue;
if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
(achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
continue;
// don't update already completed criteria
if (IsCompletedCriteria(achievementCriteria,achievement))
continue;
// if we have additional DB criteria, they must be met to start the criteria
AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
if (data && !data->Meets(GetPlayer(), NULL)) // TODO this might need more research, if this could be the player, or we also need to pass an unit
continue;
// do not start already failed timers
if (startTime && time_t(startTime + achievementCriteria->timeLimit) < time(NULL))
continue;
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);
SendCriteriaUpdate(achievementCriteria->ID, progress);
}
}
/** /**
* this function will be called whenever the user might have done a criteria relevant action * this function will be called whenever the user might have done a criteria relevant action
*/ */
@ -1102,9 +1166,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
switch(achievement->ID) switch(achievement->ID)
{ {
case 31: case 31:
case 1275: //case 1275: // these timed achievements have to be "started" on Quest Accecpt
case 1276: //case 1276:
case 1277: //case 1277:
case 1282: case 1282:
case 1789: case 1789:
{ {
@ -1812,17 +1876,21 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri
CriteriaProgress *progress = NULL; CriteriaProgress *progress = NULL;
uint32 old_value = 0; uint32 old_value = 0;
uint32 newValue = 0;
CriteriaProgressMap::iterator iter = m_criteriaProgress.find(criteria->ID); CriteriaProgressMap::iterator iter = m_criteriaProgress.find(criteria->ID);
if(iter == m_criteriaProgress.end()) if(iter == m_criteriaProgress.end())
{ {
// not create record for 0 counter // not create record for 0 counter
if(changeValue == 0) if(changeValue == 0)
return; return;
// not start manually started timed achievements - must be same check as below (ie possible on player logout between start and finish)
if (criteria->IsExplicitlyStartedTimedCriteria())
return;
progress = &m_criteriaProgress[criteria->ID]; progress = &m_criteriaProgress[criteria->ID];
progress->counter = changeValue; newValue = changeValue;
progress->date = time(NULL); progress->date = time(NULL);
} }
else else
@ -1830,8 +1898,6 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri
progress = &iter->second; progress = &iter->second;
old_value = progress->counter; old_value = progress->counter;
uint32 newValue = 0;
switch(ptype) switch(ptype)
{ {
case PROGRESS_SET: case PROGRESS_SET:
@ -1851,8 +1917,6 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri
// not update (not mark as changed) if counter will have same value // not update (not mark as changed) if counter will have same value
if(progress->counter == newValue) if(progress->counter == newValue)
return; return;
progress->counter = newValue;
} }
progress->changed = true; progress->changed = true;
@ -1860,12 +1924,24 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteri
if(criteria->timeLimit) if(criteria->timeLimit)
{ {
time_t now = time(NULL); 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 // 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->date = now;
} }
}
}
progress->counter = newValue;
progress->changed = true;
// update client side value // update client side value
SendCriteriaUpdate(criteria->ID,progress); SendCriteriaUpdate(criteria->ID,progress);

View file

@ -248,6 +248,7 @@ class AchievementMgr
void LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult); void LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult);
void SaveToDB(); void SaveToDB();
void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0); void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0);
void StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime = 0);
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0);
void CheckAllAchievementCriteria(); void CheckAllAchievementCriteria();
void SendAllAchievementData(); void SendAllAchievementData();

View file

@ -481,7 +481,7 @@ struct AchievementCriteriaEntry
struct struct
{ {
uint32 field3; // 3 main requirement uint32 value; // 3 main requirement
uint32 count; // 4 main requirement count uint32 count; // 4 main requirement count
uint32 additionalRequirement1_type; // 5 additional requirement 1 type uint32 additionalRequirement1_type; // 5 additional requirement 1 type
uint32 additionalRequirement1_value; // 6 additional requirement 1 value uint32 additionalRequirement1_value; // 6 additional requirement 1 value
@ -493,11 +493,19 @@ struct AchievementCriteriaEntry
//uint32 name_flags; // 25 //uint32 name_flags; // 25
uint32 completionFlag; // 26 uint32 completionFlag; // 26
uint32 groupFlag; // 27 uint32 groupFlag; // 27
//uint32 unk1; // 28 Alway appears with timed events uint32 timedCriteriaMiscId; // 28 Alway appears with timed events, used internally to start the achievement, store
// for timed spells it is spell id for
// timed kills it is creature id
uint32 timeLimit; // 29 time limit in seconds uint32 timeLimit; // 29 time limit in seconds
uint32 showOrder; // 30 show order, also used in achievement shift-links as index in state bitmask uint32 showOrder; // 30 show order, also used in achievement shift-links as index in state bitmask
// helpers
bool IsExplicitlyStartedTimedCriteria() const
{
if (!timeLimit)
return false;
// in case raw.value == timedCriteriaMiscId in timedCriteriaMiscId stored spellid/itemids for cast/use, so repeating aura start at first cast/use until fails
return requiredType == ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST || raw.value != timedCriteriaMiscId;
}
}; };
struct AreaTableEntry struct AreaTableEntry

View file

@ -20,7 +20,7 @@
#define MANGOS_DBCSFRM_H #define MANGOS_DBCSFRM_H
const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
const char AchievementCriteriafmt[]="niiiiiiiissssssssssssssssxiixii"; const char AchievementCriteriafmt[]="niiiiiiiissssssssssssssssxiiiii";
const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx"; const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx";
const char AreaGroupEntryfmt[]="niiiiiii"; const char AreaGroupEntryfmt[]="niiiiiii";
const char AreaTriggerEntryfmt[]="niffffffff"; const char AreaTriggerEntryfmt[]="niffffffff";

View file

@ -21331,6 +21331,11 @@ void Player::UpdateAchievementCriteria( AchievementCriteriaTypes type, uint32 mi
GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1,miscvalue2,unit,time); GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1,miscvalue2,unit,time);
} }
void Player::StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime /*= 0*/)
{
GetAchievementMgr().StartTimedAchievementCriteria(type, timedRequirementId, startTime);
}
PlayerTalent const* Player::GetKnownTalentById(int32 talentId) const PlayerTalent const* Player::GetKnownTalentById(int32 talentId) const
{ {
PlayerTalentMap::const_iterator itr = m_talents[m_activeSpec].find(talentId); PlayerTalentMap::const_iterator itr = m_talents[m_activeSpec].find(talentId);

View file

@ -2385,6 +2385,8 @@ class MANGOS_DLL_SPEC Player : public Unit
AchievementMgr const& GetAchievementMgr() const { return m_achievementMgr; } AchievementMgr const& GetAchievementMgr() const { return m_achievementMgr; }
AchievementMgr& GetAchievementMgr() { return m_achievementMgr; } AchievementMgr& GetAchievementMgr() { return m_achievementMgr; }
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0);
void StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime = 0);
bool HasTitle(uint32 bitIndex); bool HasTitle(uint32 bitIndex);
bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); } bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); }

View file

@ -189,6 +189,8 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
if ( _player->CanCompleteQuest( quest ) ) if ( _player->CanCompleteQuest( quest ) )
_player->CompleteQuest( quest ); _player->CompleteQuest( quest );
_player->GetAchievementMgr().StartTimedAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, quest);
switch(pObject->GetTypeId()) switch(pObject->GetTypeId())
{ {
case TYPEID_UNIT: case TYPEID_UNIT:

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "10351" #define REVISION_NR "10352"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__