[10342] Cleanup achievement code and add new commands.

* .achievement - let see achievement state and list criteria with progress data.
                 It provide criteria ids/shift-links for other commands.
* .achievement add - let complete achivement (set all criteria progress to max) with related events.
* .achievement remove - let reset achievement  criteria progress and undo complete state for achievement.
                 Command also partly remove rewards (title part).
* .achievement criteria add - let increase criteria progress at specific amount or to complete state
* .achievement criteria remove - let reset/descrease criteria progress and undo criteria and related achivement complete state if need.
This commit is contained in:
VladimirMangos 2010-08-11 05:14:03 +04:00
parent 5c2fc55fe4
commit 4c838a1c31
14 changed files with 701 additions and 184 deletions

View file

@ -430,12 +430,12 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uin
case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
if (achievementCriteria->healing_done.flag == miscvalue1 &&
achievementCriteria->healing_done.mapid == miscvalue2)
SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
SetCriteriaProgress(achievementCriteria, achievement, 0, PROGRESS_SET);
break;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
// reset only the criteria having the miscvalue1 condition
if (achievementCriteria->win_rated_arena.flag == miscvalue1)
SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
SetCriteriaProgress(achievementCriteria, achievement, 0, PROGRESS_SET);
break;
default: // reset all cases
break;
@ -690,6 +690,9 @@ static const uint32 achievIdForDangeon[][4] =
{ 0, false, false, false }
};
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 };
/**
* this function will be called whenever the user might have done a criteria relevant action
*/
@ -720,6 +723,10 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (IsCompletedCriteria(achievementCriteria,achievement))
continue;
// init values, real set in switch
uint32 change = 0;
ProgressType progressType = PROGRESS_SET;
switch (type)
{
// std. case: increment at 1
@ -736,7 +743,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
// std case: increment at miscvalue1
case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
@ -752,7 +760,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
change = miscvalue1;
progressType = PROGRESS_ACCUMULATE;
break;
// std case: high value at miscvalue1
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
@ -764,7 +773,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
change = miscvalue1;
progressType = PROGRESS_HIGHEST;
break;
// specialized cases
@ -814,7 +824,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
}
}
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
change = miscvalue1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
@ -830,37 +841,79 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data || !data->Meets(GetPlayer(),unit))
continue;
SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
change = miscvalue2;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
{
bool ok = true;
// skip wrong class achievements
for(uint8 i = 1; i < MAX_CLASSES; ++i)
{
if (achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
{
ok = false;
break;
}
}
if (!ok)
continue;
// skip wrong race achievements
for(uint8 i = 1; i < MAX_RACES; ++i)
{
if (achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
{
ok = false;
break;
}
}
if (!ok)
continue;
change = GetPlayer()->getLevel();
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
{
// update at loading or specific skill update
if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
continue;
if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
SetCriteriaProgress(achievementCriteria, skillvalue);
change = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID);
progressType = PROGRESS_HIGHEST;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
{
// update at loading or specific skill update
if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
continue;
if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
SetCriteriaProgress(achievementCriteria, maxSkillvalue);
change = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID);
progressType = PROGRESS_HIGHEST;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
SetCriteriaProgress(achievementCriteria, 1);
{
if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) == m_completedAchievements.end())
continue;
change = 1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
{
uint32 counter =0;
for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
if(itr->second.m_rewarded)
counter++;
SetCriteriaProgress(achievementCriteria, counter);
change = counter;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
@ -876,7 +929,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
counter++;
}
SetCriteriaProgress(achievementCriteria, counter);
change = counter;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
@ -885,7 +939,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
continue;
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
change = miscvalue1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
@ -893,7 +948,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
{
@ -916,7 +972,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(notfit)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
@ -964,7 +1021,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
//FIXME: work only for instances where max==min for players
if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
@ -974,7 +1033,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
@ -985,7 +1045,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
{
@ -999,7 +1060,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
// miscvalue1 is the ingame fallheight*100 as stored in dbc
SetCriteriaProgress(achievementCriteria, miscvalue1);
change = miscvalue1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
@ -1008,7 +1070,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(miscvalue2 != achievementCriteria->death_from.type)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
{
@ -1046,7 +1109,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
}
SetCriteriaProgress(achievementCriteria, 1);
change = 1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
@ -1063,7 +1127,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data->Meets(GetPlayer(),unit))
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
@ -1080,15 +1145,19 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data->Meets(GetPlayer(),unit))
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
continue;
if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
SetCriteriaProgress(achievementCriteria, 1);
if(!GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
continue;
change = 1;
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
{
@ -1108,14 +1177,16 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
}
SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
change = miscvalue2;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
// speedup for non-login case
if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
change = GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true);
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
// miscvalue1 contains the personal rating
@ -1130,12 +1201,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
{
// reset the progress as we have a win without the requirement.
SetCriteriaProgress(achievementCriteria, 0);
SetCriteriaProgress(achievementCriteria, achievement, 0);
continue;
}
}
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
@ -1143,7 +1215,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(achievementCriteria->use_item.itemID != miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
// You _have_ to loot that item, just owning it when logging in does _not_ count!
@ -1151,7 +1224,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(miscvalue1 != achievementCriteria->own_item.itemID)
continue;
SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
change = miscvalue2;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
{
@ -1180,12 +1254,16 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
}
}
if(matchFound)
SetCriteriaProgress(achievementCriteria, 1);
if(!matchFound)
continue;
change = 1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
change = GetPlayer()->GetBankBagSlotCount();
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
{
@ -1194,13 +1272,17 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
if (reputation > 0)
SetCriteriaProgress(achievementCriteria, reputation);
if (!reputation)
continue;
change = reputation;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
{
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
change = GetPlayer()->GetReputationMgr().GetExaltedFactionCount();
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
@ -1208,7 +1290,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// skip for login case
if(!miscvalue1)
continue;
SetCriteriaProgress(achievementCriteria, 1);
change = 1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
@ -1223,7 +1306,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
if(!data || !data->Meets(GetPlayer(),unit,item_slot))
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_SET);
change = 1;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
@ -1243,7 +1327,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!pProto || pProto->ItemLevel <requiredItemLevel)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
@ -1261,7 +1346,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
}
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
@ -1280,7 +1366,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
}
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
change = miscvalue1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
@ -1290,7 +1377,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (miscvalue1 != achievementCriteria->equip_item.itemID)
continue;
SetCriteriaProgress(achievementCriteria, 1);
change = 1;
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
// miscvalue1 = go entry
@ -1299,7 +1387,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
{
@ -1315,7 +1404,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (!data->Meets(GetPlayer(),unit))
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
@ -1324,7 +1414,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
{
@ -1343,7 +1434,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
spellCount++;
}
}
SetCriteriaProgress(achievementCriteria, spellCount);
change = spellCount;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
@ -1362,16 +1454,20 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
}
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
change = GetPlayer()->GetReputationMgr().GetReveredFactionCount();
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
change = GetPlayer()->GetReputationMgr().GetHonoredFactionCount();
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
change = GetPlayer()->GetReputationMgr().GetVisibleFactionCount();
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
@ -1382,7 +1478,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1);
if (!proto || proto->Quality < ITEM_QUALITY_EPIC)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
@ -1400,26 +1497,31 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
spellCount++;
}
SetCriteriaProgress(achievementCriteria, spellCount);
change = spellCount;
progressType = PROGRESS_SET;
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
change = GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS);
progressType = PROGRESS_SET;
break;
case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
change = 1;
progressType = PROGRESS_ACCUMULATE;
break;
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
change = GetPlayer()->GetMoney();
progressType = PROGRESS_HIGHEST;
break;
// std case: not exist in DBC, not triggered in code as result
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
@ -1447,147 +1549,99 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
break; // Not implemented yet :(
}
if(IsCompletedCriteria(achievementCriteria,achievement))
CompletedCriteriaFor(achievement);
// check again the completeness for SUMM and REQ COUNT achievements,
// as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
{
if (IsCompletedAchievement(achievement))
CompletedAchievement(achievement);
}
if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
{
for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
if(IsCompletedAchievement(*itr))
CompletedAchievement(*itr);
}
SetCriteriaProgress(achievementCriteria, achievement, change, progressType);
}
}
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 };
bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) const
uint32 AchievementMgr::GetCriteriaProgressMaxCounter(AchievementCriteriaEntry const* achievementCriteria)
{
// counter can never complete
if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return false;
if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
{
// someone on this realm has already completed that achievement
if(sAchievementMgr.IsRealmCompleted(achievement))
return false;
}
CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
if(itr == m_criteriaProgress.end())
return false;
CriteriaProgress const* progress = &itr->second;
switch(achievementCriteria->requiredType)
{
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
return progress->counter >= achievementCriteria->win_bg.winCount;
return achievementCriteria->win_bg.winCount;
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
return progress->counter >= achievementCriteria->kill_creature.creatureCount;
return achievementCriteria->kill_creature.creatureCount;
case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
{
// skip wrong class achievements
for(int i = 1; i < MAX_CLASSES; ++i)
if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
return false;
// skip wrong race achievements
for(int i = 1; i < MAX_RACES; ++i)
if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
return false;
// appropriate class/race or not class/race specific
return progress->counter >= achievementCriteria->reach_level.level;
}
return achievementCriteria->reach_level.level;
case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
return achievementCriteria->reach_skill_level.skillLevel;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
return progress->counter >= 1;
return 1;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
return achievementCriteria->complete_quest_count.totalQuestCount;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
return achievementCriteria->complete_quests_in_zone.questCount;
case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
return progress->counter >= achievementCriteria->healing_done.count;
return achievementCriteria->healing_done.count;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
return achievementCriteria->complete_daily_quest.questCount;
case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
return achievementCriteria->fall_without_dying.fallHeight;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
return progress->counter >= 1;
return 1;
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
return progress->counter >= achievementCriteria->be_spell_target.spellCount;
return achievementCriteria->be_spell_target.spellCount;
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
return progress->counter >= achievementCriteria->cast_spell.castCount;
return achievementCriteria->cast_spell.castCount;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
return progress->counter >= 1;
return 1;
case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
return progress->counter >= achievementCriteria->own_item.itemCount;
return achievementCriteria->own_item.itemCount;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
return progress->counter >= achievementCriteria->win_rated_arena.count;
return achievementCriteria->win_rated_arena.count;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
return achievementCriteria->learn_skill_level.skillLevel * 75;
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
return progress->counter >= achievementCriteria->use_item.itemCount;
return achievementCriteria->use_item.itemCount;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
return progress->counter >= achievementCriteria->loot_item.itemCount;
return achievementCriteria->loot_item.itemCount;
case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
return progress->counter >= 1;
return 1;
case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
return achievementCriteria->buy_bank_slot.numberOfSlots;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
return achievementCriteria->gain_reputation.reputationAmount;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
return achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
return achievementCriteria->visit_barber.numberOfVisits;
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
return progress->counter >= achievementCriteria->equip_epic_item.count;
return achievementCriteria->equip_epic_item.count;
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
return achievementCriteria->roll_greed_on_loot.count;
case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
return progress->counter >= achievementCriteria->hk_class.count;
return achievementCriteria->hk_class.count;
case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
return progress->counter >= achievementCriteria->hk_race.count;
return achievementCriteria->hk_race.count;
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
return progress->counter >= achievementCriteria->do_emote.count;
return achievementCriteria->do_emote.count;
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
return progress->counter >= achievementCriteria->equip_item.count;
return achievementCriteria->equip_item.count;
case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
return achievementCriteria->quest_reward_money.goldInCopper;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
return progress->counter >= achievementCriteria->loot_money.goldInCopper;
return achievementCriteria->loot_money.goldInCopper;
case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
return progress->counter >= achievementCriteria->use_gameobject.useCount;
return achievementCriteria->use_gameobject.useCount;
case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
return progress->counter >= achievementCriteria->special_pvp_kill.killCount;
return achievementCriteria->special_pvp_kill.killCount;
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
return achievementCriteria->fish_in_gameobject.lootCount;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
return achievementCriteria->learn_skillline_spell.spellCount;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
return progress->counter >= achievementCriteria->win_duel.duelCount;
return achievementCriteria->win_duel.duelCount;
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
return achievementCriteria->loot_type.lootTypeCount;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
return achievementCriteria->learn_skill_line.spellCount;
case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
return progress->counter >= achievementCriteria->honorable_kill.killCount;
return achievementCriteria->honorable_kill.killCount;
// handle all statistic-only criteria here
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
@ -1622,9 +1676,38 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
return 0;
}
return 0;
}
bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) const
{
// counter can never complete
if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return false;
if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
{
// someone on this realm has already completed that achievement
if(sAchievementMgr.IsRealmCompleted(achievement))
return false;
}
return false;
CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
if(itr == m_criteriaProgress.end())
return false;
CriteriaProgress const* progress = &itr->second;
uint32 maxcounter = GetCriteriaProgressMaxCounter(achievementCriteria);
// different counters or non-completable criteria
if (!maxcounter)
return false;
return progress->counter >= maxcounter;
}
void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
@ -1704,13 +1787,23 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
return false;
}
void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 changeValue, ProgressType ptype)
{
DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", criteria->ID, changeValue, m_player->GetGUIDLow());
uint32 max_value = GetCriteriaProgressMaxCounter(criteria);
if (!max_value)
max_value = std::numeric_limits<uint32>::max();
// change value must be in allowed value range for SET/HIGHEST directly
if (changeValue > max_value)
changeValue = max_value;
CriteriaProgress *progress = NULL;
uint32 old_value = 0;
CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
CriteriaProgressMap::iterator iter = m_criteriaProgress.find(criteria->ID);
if(iter == m_criteriaProgress.end())
{
@ -1718,7 +1811,7 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
if(changeValue == 0)
return;
progress = &m_criteriaProgress[entry->ID];
progress = &m_criteriaProgress[criteria->ID];
progress->counter = changeValue;
progress->date = time(NULL);
}
@ -1726,6 +1819,8 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
{
progress = &iter->second;
old_value = progress->counter;
uint32 newValue = 0;
switch(ptype)
{
@ -1735,7 +1830,6 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
case PROGRESS_ACCUMULATE:
{
// avoid overflow
uint32 max_value = std::numeric_limits<uint32>::max();
newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
break;
}
@ -1753,16 +1847,64 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
progress->changed = true;
if(entry->timeLimit)
if(criteria->timeLimit)
{
time_t now = time(NULL);
if(time_t(progress->date + entry->timeLimit) < now)
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;
}
SendCriteriaUpdate(entry->ID,progress);
// update client side value
SendCriteriaUpdate(criteria->ID,progress);
// nothing do for counter case
if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return;
// update dependent achievements state at criteria complete
if (old_value < progress->counter)
{
if(IsCompletedCriteria(criteria, achievement))
CompletedCriteriaFor(achievement);
// check again the completeness for SUMM and REQ COUNT achievements,
// as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
{
if (IsCompletedAchievement(achievement))
CompletedAchievement(achievement);
}
if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
{
for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
if(IsCompletedAchievement(*itr))
CompletedAchievement(*itr);
}
}
// update dependent achievements state at criteria incomplete
else if (old_value > progress->counter)
{
if (progress->counter < max_value)
{
WorldPacket data(SMSG_CRITERIA_DELETED,4);
data << uint32(criteria->ID);
m_player->SendDirectMessage(&data);
}
if (HasAchievement(achievement->ID))
if(!IsCompletedAchievement(achievement))
IncompletedAchievement(achievement);
if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
if (HasAchievement((*itr)->ID))
if(!IsCompletedAchievement(*itr))
IncompletedAchievement(*itr);
}
}
void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
@ -1833,6 +1975,43 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
}
}
void AchievementMgr::IncompletedAchievement(AchievementEntry const* achievement)
{
DETAIL_LOG("AchievementMgr::IncompletedAchievement(%u)", achievement->ID);
if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return;
CompletedAchievementMap::iterator itr = m_completedAchievements.find(achievement->ID);
if (itr == m_completedAchievements.end())
return;
WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
data << uint32(achievement->ID);
m_player->SendDirectMessage(&data);
if (!itr->second.changed) // complete state saved
CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u AND achievement = %u",
GetPlayer()->GetGUIDLow(), achievement->ID);
m_completedAchievements.erase(achievement->ID);
// reward items and titles if any
AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement, GetPlayer()->getGender());
// no rewards
if(!reward)
return;
// titles
if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
{
if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry, true);
}
// items impossible remove in clear way...
}
void AchievementMgr::SendAllAchievementData()
{
// since we don't know the exact size of the packed GUIDs this is just an approximation