diff options
Diffstat (limited to 'src/game/AchievementMgr.cpp')
-rw-r--r-- | src/game/AchievementMgr.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index 50daa5c565f..c9b98c3ddef 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -15,6 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "DBCEnums.h" #include "ObjectMgr.h" @@ -22,6 +23,7 @@ #include "WorldPacket.h" #include "Database/DatabaseEnv.h" #include "Policies/SingletonImp.h" + #include "AchievementMgr.h" #include "ArenaTeam.h" #include "CellImpl.h" @@ -32,11 +34,14 @@ #include "Player.h" #include "ProgressBar.h" #include "SpellMgr.h" + #include "MapManager.h" #include "BattleGround.h" #include "BattleGroundAB.h" + INSTANTIATE_SINGLETON_1(AchievementGlobalMgr); + namespace MaNGOS { class AchievementChatBuilder @@ -47,6 +52,7 @@ namespace MaNGOS void operator()(WorldPacket& data, int32 loc_idx) { char const* text = objmgr.GetMangosString(i_textId,loc_idx); + data << uint8(i_msgtype); data << uint32(LANG_UNIVERSAL); data << uint64(i_player.GetGUID()); @@ -57,6 +63,7 @@ namespace MaNGOS data << uint8(0); data << uint32(i_achievementId); } + private: Player const& i_player; ChatMsg i_msgtype; @@ -65,6 +72,7 @@ namespace MaNGOS }; } // namespace MaNGOS + bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) { if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) @@ -72,6 +80,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType); return false; } + switch(criteria->requiredType) { case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: @@ -89,6 +98,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType); return false; } + switch(dataType) { case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: @@ -235,6 +245,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) } return false; } + bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const { switch(dataType) @@ -306,20 +317,25 @@ bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, ui } return false; } + bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const { for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) if(!itr->Meets(source,target,miscvalue)) return false; + return true; } + AchievementMgr::AchievementMgr(Player *player) { m_player = player; } + AchievementMgr::~AchievementMgr() { } + void AchievementMgr::Reset() { for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) @@ -328,34 +344,43 @@ void AchievementMgr::Reset() data << uint32(iter->first); m_player->SendDirectMessage(&data); } + for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) { WorldPacket data(SMSG_CRITERIA_DELETED,4); data << uint32(iter->first); m_player->SendDirectMessage(&data); } + m_completedAchievements.clear(); m_criteriaProgress.clear(); DeleteFromDB(m_player->GetGUIDLow()); + // re-fill data CheckAllAchievementCriteria(); } + void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2) { if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2); + if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) return; + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) { AchievementCriteriaEntry const *achievementCriteria = (*i); + AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); if (!achievement) continue; + // don't update already completed criteria if (IsCompletedCriteria(achievementCriteria,achievement)) continue; + switch (type) { case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset @@ -374,6 +399,7 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uin } } } + void AchievementMgr::DeleteFromDB(uint32 lowguid) { CharacterDatabase.BeginTransaction (); @@ -381,6 +407,7 @@ void AchievementMgr::DeleteFromDB(uint32 lowguid) CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid); CharacterDatabase.CommitTransaction (); } + void AchievementMgr::SaveToDB() { if(!m_completedAchievements.empty()) @@ -392,6 +419,7 @@ void AchievementMgr::SaveToDB() { if(!iter->second.changed) continue; + /// first new/changed record prefix if(!need_execute) { @@ -405,20 +433,25 @@ void AchievementMgr::SaveToDB() ssdel << ", "; ssins << ", "; } + // new/changed record data ssdel << iter->first; ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; + /// mark as saved in db iter->second.changed = false; } + if(need_execute) ssdel << ")"; + if(need_execute) { CharacterDatabase.Execute( ssdel.str().c_str() ); CharacterDatabase.Execute( ssins.str().c_str() ); } } + if(!m_criteriaProgress.empty()) { /// prepare deleting and insert @@ -430,6 +463,7 @@ void AchievementMgr::SaveToDB() { if(!iter->second.changed) continue; + // deleted data (including 0 progress state) { /// first new/changed record prefix (for any counter value) @@ -441,9 +475,11 @@ void AchievementMgr::SaveToDB() /// next new/changed record prefix else ssdel << ", "; + // new/changed record data ssdel << iter->first; } + // store data only for real progress if(iter->second.counter != 0) { @@ -456,14 +492,18 @@ void AchievementMgr::SaveToDB() /// next new/changed record prefix else ssins << ", "; + // new/changed record data ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")"; } + /// mark as updated in db iter->second.changed = false; } + if(need_execute_del) // DELETE ... IN (.... _)_ ssdel << ")"; + if(need_execute_del || need_execute_ins) { if(need_execute_del) @@ -473,6 +513,7 @@ void AchievementMgr::SaveToDB() } } } + void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult) { if(achievementResult) @@ -480,24 +521,30 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri do { Field *fields = achievementResult->Fetch(); + uint32 achievement_id = fields[0].GetUInt32(); + // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements() if(!sAchievementStore.LookupEntry(achievement_id)) continue; + CompletedAchievementData& ca = m_completedAchievements[achievement_id]; ca.date = time_t(fields[1].GetUInt64()); ca.changed = false; } while(achievementResult->NextRow()); delete achievementResult; } + if(criteriaResult) { do { Field *fields = criteriaResult->Fetch(); + uint32 id = fields[0].GetUInt32(); uint32 counter = fields[1].GetUInt32(); time_t date = time_t(fields[2].GetUInt64()); + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id); if (!criteria) { @@ -506,8 +553,10 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id); continue; } + if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL)) continue; + CriteriaProgress& progress = m_criteriaProgress[id]; progress.counter = counter; progress.date = date; @@ -515,21 +564,26 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri } while(criteriaResult->NextRow()); delete criteriaResult; } + } + void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) { if(GetPlayer()->GetSession()->PlayerLoading()) return; + #ifdef TRINITY_DEBUG if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID); #endif + if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId())) { MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder); guild->BroadcastWorker(say_do,GetPlayer()); } + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) { // broadcast realm first reached @@ -543,9 +597,11 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) else { CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); + Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); + MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder); MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do); @@ -553,6 +609,7 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY)); } + WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); data.append(GetPlayer()->GetPackGUID()); data << uint32(achievement->ID); @@ -560,12 +617,15 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) data << uint32(0); GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true); } + void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress) { WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); data << uint32(id); + // the counter is packed like a packed Guid data.appendPackGUID(progress->counter); + data.append(GetPlayer()->GetPackGUID()); data << uint32(0); data << uint32(secsToTimeBitFields(progress->date)); @@ -573,6 +633,7 @@ void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progr data << uint32(0); // timer 2 GetPlayer()->SendDirectMessage(&data); } + /** * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet */ @@ -582,6 +643,7 @@ void AchievementMgr::CheckAllAchievementCriteria() for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i) UpdateAchievementCriteria(AchievementCriteriaTypes(i)); } + static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 }; static const uint32 achievIdForDangeon[][4] = { @@ -593,6 +655,7 @@ static const uint32 achievIdForDangeon[][4] = { 2219, false, false, true }, { 0, false, false, false } }; + /** * this function will be called whenever the user might have done a criteria relevant action */ @@ -600,23 +663,30 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time); + if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) return; + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) { 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; + 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; + switch (type) { // std. case: increment at 1 @@ -659,7 +729,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); break; + // specialized cases + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: { // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -667,6 +739,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) continue; + if (achievementCriteria->win_bg.additionalRequirement1_type) { // those requirements couldn't be found in the dbc @@ -680,6 +753,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui BattleGround* bg = GetPlayer()->GetBattleGround(); if (!bg) continue; + switch(achievementCriteria->referredAchievement) { case 161: // AB, Overcome a 500 resource disadvantage @@ -702,6 +776,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; // not implemented } } + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); break; } @@ -712,10 +787,12 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(achievementCriteria->kill_creature.creatureID != miscvalue1) continue; + // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); if(!data || !data->Meets(GetPlayer(),unit)) continue; + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); break; } @@ -754,6 +831,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // speedup for non-login case if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID) continue; + uint32 counter =0; for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++) { @@ -794,11 +872,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui BattleGround* bg = GetPlayer()->GetBattleGround(); if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) notfit = true; + break; } } if(notfit) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } @@ -807,9 +887,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; + Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); if(!map || !map->IsDungeon()) continue; + // search case bool found = false; for(int j = 0; achievIdForDangeon[j][0]; ++j) @@ -834,17 +916,20 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!achievIdForDangeon[j][3]) break; // for } + found = true; break; // for } } if(!found) continue; + //FIXME: work only for instances where max==min for players if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) continue; SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; + } case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -858,9 +943,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; + // if team check required: must kill by opposition faction if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam()) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: @@ -868,10 +955,12 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; + // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); if(!data || !data->Meets(GetPlayer(),unit)) continue; + // miscvalue1 is the ingame fallheight*100 as stored in dbc SetCriteriaProgress(achievementCriteria, miscvalue1); break; @@ -898,6 +987,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) continue; } + // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case switch(achievement->ID) { @@ -918,6 +1008,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; } + SetCriteriaProgress(achievementCriteria, 1); break; } @@ -932,18 +1023,22 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) continue; + // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); if(!data) continue; + if(!data->Meets(GetPlayer(),unit)) continue; + SetCriteriaProgress(achievementCriteria, 1, 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); break; @@ -955,6 +1050,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if (miscvalue1 != achievementCriteria->loot_type.lootType) continue; + // zone specific if(achievementCriteria->loot_type.lootTypeCount==1) { @@ -963,6 +1059,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data || !data->Meets(GetPlayer(),unit)) continue; } + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); break; } @@ -976,6 +1073,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // miscvalue1 contains the personal rating if (!miscvalue1) // no update at login continue; + // additional requirements if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE) { @@ -988,6 +1086,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; } } + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: @@ -1011,23 +1110,28 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); if(!worldOverlayEntry) break; + bool matchFound = false; for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) { uint32 area_id = worldOverlayEntry->areatableID[j]; if(!area_id) // array have 0 only in empty tail break; + int32 exploreFlag = GetAreaFlagByAreaID(area_id); if(exploreFlag < 0) continue; + uint32 playerIndexOffset = uint32(exploreFlag) / 32; uint32 mask = 1<< (uint32(exploreFlag) % 32); + if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) { matchFound = true; break; } } + if(matchFound) SetCriteriaProgress(achievementCriteria, 1); break; @@ -1040,6 +1144,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // skip faction check only at loading if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID) continue; + int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); if (reputation > 0) SetCriteriaProgress(achievementCriteria, reputation); @@ -1068,9 +1173,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) continue; ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 ); + uint32 requiredItemLevel = 0; if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358) requiredItemLevel = 185; + if(!pProto || pProto->ItemLevel <requiredItemLevel) continue; SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); @@ -1090,6 +1197,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data || !data->Meets(GetPlayer(),unit)) continue; } + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } @@ -1098,14 +1206,17 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { if (!miscvalue1) continue; + if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP) { if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid) continue; + // map specific case (BG in fact) expected player targeted damage/heal if(!unit || unit->GetTypeId()!=TYPEID_PLAYER) continue; } + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); break; } @@ -1115,6 +1226,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if (miscvalue1 != achievementCriteria->equip_item.itemID) continue; + SetCriteriaProgress(achievementCriteria, 1); break; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: @@ -1123,6 +1235,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if (miscvalue1 != achievementCriteria->use_gameobject.goEntry) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: @@ -1130,12 +1243,14 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: { if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine) continue; + uint32 spellCount = 0; for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); spellIter != GetPlayer()->GetSpellMap().end(); @@ -1155,15 +1270,18 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if (!miscvalue1) continue; + if (achievementCriteria->win_duel.duelCount) { // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); if (!data) continue; + if (!data->Meets(GetPlayer(),unit)) continue; } + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: @@ -1179,6 +1297,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine) continue; + uint32 spellCount = 0; for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); spellIter != GetPlayer()->GetSpellMap().end(); @@ -1198,11 +1317,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID) continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: @@ -1243,6 +1364,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } 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) @@ -1250,6 +1372,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (IsCompletedAchievement(achievement)) CompletedAchievement(achievement); } + if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID)) { for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) @@ -1258,23 +1381,29 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } } } + 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) { // 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(achievementmgr.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: @@ -1287,10 +1416,12 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve 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; } @@ -1399,29 +1530,36 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve } return false; } + void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement) { // counter can never complete if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) return; + // already completed and stored if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end()) return; + if (IsCompletedAchievement(achievement)) CompletedAchievement(achievement); } + bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) { // counter can never complete if(entry->flags & ACHIEVEMENT_FLAG_COUNTER) return false; + // for achievement with referenced achievement criterias get from referenced and counter from self uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID; uint32 achievmentForTestCount = entry->count; + AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId); if(!cList) return false; uint32 count = 0; + // For SUMM achievements, we have to count the progress of each criteria of the achievement. // Oddly, the target count is NOT countained in the achievement, but in each individual criteria if (entry->flags & ACHIEVEMENT_FLAG_SUMM) @@ -1429,48 +1567,62 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) { AchievementCriteriaEntry const* criteria = *itr; + CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID); if(itrProgress == m_criteriaProgress.end()) continue; + CriteriaProgress const* progress = &itrProgress->second; count += progress->counter; + // for counters, field4 contains the main count requirement if (count >= criteria->raw.count) return true; } return false; } + // Default case - need complete all or bool completed_all = true; for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) { AchievementCriteriaEntry const* criteria = *itr; + bool completed = IsCompletedCriteria(criteria,entry); + // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL if(completed) ++count; else completed_all = false; + // completed as have req. count of completed criterias if(achievmentForTestCount > 0 && achievmentForTestCount <= count) return true; } + // all criterias completed requirement if(completed_all && achievmentForTestCount==0) return true; + return false; } + void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) { if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow()); + CriteriaProgress *progress = NULL; + CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); + if(iter == m_criteriaProgress.end()) { // not create record for 0 counter if(changeValue == 0) return; + progress = &m_criteriaProgress[entry->ID]; progress->counter = changeValue; progress->date = time(NULL); @@ -1478,6 +1630,7 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, else { progress = &iter->second; + uint32 newValue = 0; switch(ptype) { @@ -1495,62 +1648,81 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, newValue = progress->counter < changeValue ? changeValue : progress->counter; break; } + // not update (not mark as changed) if counter will have same value if(progress->counter == newValue) return; + progress->counter = newValue; } + progress->changed = true; + if(entry->timeLimit) { time_t now = time(NULL); if(time_t(progress->date + entry->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); } + void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) { sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID); + if(!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) return; + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end()) return; + SendAchievementEarned(achievement); CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; ca.date = time(NULL); ca.changed = true; + // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement // TODO: where do set this instead? if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) achievementmgr.SetRealmCompleted(achievement); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT); + // reward items and titles if any AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement); + // 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); } + // mail if(reward->sender) { Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL; + MailItemsInfo mi; if(item) { // save new item before send item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted + // item mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); } + int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); + // subject and text std::string subject = reward->subject; std::string text = reward->text; @@ -1564,28 +1736,36 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) text = loc->text[loc_idx]; } } + uint32 itemTextId = objmgr.CreateItemText( text ); + WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE); } } + void AchievementMgr::SendAllAchievementData() { uint32 size = 18 + m_completedAchievements.size()*8 + m_criteriaProgress.size() * 36; + bool send = false; + WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA); if( size < 0x8000 ) data.resize( size ); else data.resize( 0x7fff ); // More than this causes client trouble + CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); CriteriaProgressMap::const_iterator iter2 = m_criteriaProgress.begin(); + bool cAchievements = false; bool cProgress = false; while( !cAchievements || !cProgress ) { data.clear(); send = false; + if( !cAchievements ) { for(; iter != m_completedAchievements.end() && !send; ++iter) @@ -1594,9 +1774,11 @@ void AchievementMgr::SendAllAchievementData() data << uint32(secsToTimeBitFields(iter->second.date)); send = data.size() > 0x7f00; } + if( iter == m_completedAchievements.end() ) cAchievements = true; } + data << int32(-1); for(; iter2 != m_criteriaProgress.end() && !send; ++iter2) { @@ -1609,12 +1791,15 @@ void AchievementMgr::SendAllAchievementData() data << uint32(0); send = data.size() > 0x7f00; } + if( iter2 == m_criteriaProgress.end() ) cProgress = true; + data << int32(-1); GetPlayer()->GetSession()->SendPacket(&data); } } + void AchievementMgr::SendRespondInspectAchievements(Player* player) { // since we don't know the exact size of the packed GUIDs this is just an approximation @@ -1623,6 +1808,7 @@ void AchievementMgr::SendRespondInspectAchievements(Player* player) BuildAllDataPacket(&data); player->GetSession()->SendPacket(&data); } + /** * used by SMSG_RESPOND_INSPECT_ACHIEVEMENT */ @@ -1634,6 +1820,7 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket *data) *data << uint32(secsToTimeBitFields(iter->second.date)); } *data << int32(-1); + for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) { *data << uint32(iter->first); @@ -1644,72 +1831,91 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket *data) *data << uint32(0); *data << uint32(0); } + *data << int32(-1); } + //========================================================== AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type) { return m_AchievementCriteriasByType[type]; } + void AchievementGlobalMgr::LoadAchievementCriteriaList() { if(sAchievementCriteriaStore.GetNumRows()==0) { barGoLink bar(1); bar.step(); + sLog.outString(); sLog.outErrorDb(">> Loaded 0 achievement criteria."); return; } + barGoLink bar( sAchievementCriteriaStore.GetNumRows() ); for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) { bar.step(); + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); if(!criteria) continue; + m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria); m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria); } + sLog.outString(); sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size()); } + void AchievementGlobalMgr::LoadAchievementReferenceList() { if(sAchievementStore.GetNumRows()==0) { barGoLink bar(1); bar.step(); + sLog.outString(); sLog.outErrorDb(">> Loaded 0 achievement references."); return; } + uint32 count = 0; barGoLink bar( sAchievementStore.GetNumRows() ); for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId) { bar.step(); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId); if(!achievement || !achievement->refAchievement) continue; + m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement); ++count; } + sLog.outString(); sLog.outString(">> Loaded %u achievement references.",count); } + void AchievementGlobalMgr::LoadAchievementCriteriaData() { m_criteriaDataMap.clear(); // need for reload case + QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data"); + if(!result) { barGoLink bar(1); bar.step(); + sLog.outString(); sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty."); return; } + uint32 count = 0; uint32 disabled_count = 0; barGoLink bar(result->GetRowCount()); @@ -1718,34 +1924,45 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() bar.step(); Field *fields = result->Fetch(); uint32 criteria_id = fields[0].GetUInt32(); + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id); + if (!criteria) { sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id); continue; } + AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32()); + if (!data.IsValid(criteria)) { continue; } + // this will allocate empty data set storage AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id]; + if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED) ++disabled_count; + // add real data only for not NONE data types if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) dataSet.Add(data); + // counting data by and data types ++count; } while(result->NextRow()); + delete result; + // post loading checks for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) { AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); if(!criteria) continue; + switch(criteria->requiredType) { case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: @@ -1759,6 +1976,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); if(!achievement) continue; + // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case switch(achievement->ID) { @@ -1798,28 +2016,35 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() default: // type not use DB data, ignore continue; } + if(!GetCriteriaDataSet(criteria)) sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement); } + sLog.outString(); sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count); } + void AchievementGlobalMgr::LoadCompletedAchievements() { QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); + if(!result) { barGoLink bar(1); bar.step(); + sLog.outString(); sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty."); return; } + barGoLink bar(result->GetRowCount()); do { bar.step(); Field *fields = result->Fetch(); + uint32 achievement_id = fields[0].GetUInt32(); if(!sAchievementStore.LookupEntry(achievement_id)) { @@ -1828,30 +2053,41 @@ void AchievementGlobalMgr::LoadCompletedAchievements() CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id); continue; } + m_allCompletedAchievements.insert(achievement_id); } while(result->NextRow()); + delete result; + sLog.outString(); sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size()); } + void AchievementGlobalMgr::LoadRewards() { m_achievementRewards.clear(); // need for reload case + // 0 1 2 3 4 5 6 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward"); + if(!result) { barGoLink bar(1); + bar.step(); + sLog.outString(); sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); return; } + uint32 count = 0; barGoLink bar(result->GetRowCount()); + do { bar.step(); + Field *fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); if (!sAchievementStore.LookupEntry(entry)) @@ -1859,6 +2095,7 @@ void AchievementGlobalMgr::LoadRewards() sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry); continue; } + AchievementReward reward; reward.titleId[0] = fields[1].GetUInt32(); reward.titleId[1] = fields[2].GetUInt32(); @@ -1866,14 +2103,17 @@ void AchievementGlobalMgr::LoadRewards() reward.sender = fields[4].GetUInt32(); reward.subject = fields[5].GetCppString(); reward.text = fields[6].GetCppString(); + if ((reward.titleId[0]==0)!=(reward.titleId[1]==0)) sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) only for one from teams.", entry, reward.titleId[0], reward.titleId[1]); + // must be title or mail at least if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) { sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry); continue; } + if (reward.titleId[0]) { CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); @@ -1883,6 +2123,7 @@ void AchievementGlobalMgr::LoadRewards() reward.titleId[0] = 0; } } + if (reward.titleId[1]) { CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); @@ -1892,6 +2133,7 @@ void AchievementGlobalMgr::LoadRewards() reward.titleId[1] = 0; } } + //check mail data before item for report including wrong item case if (reward.sender) { @@ -1905,11 +2147,14 @@ void AchievementGlobalMgr::LoadRewards() { if (reward.itemId) sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry); + if (!reward.subject.empty()) sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry); + if (!reward.text.empty()) sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry); } + if (reward.itemId) { if (!objmgr.GetItemPrototype(reward.itemId)) @@ -1918,37 +2163,52 @@ void AchievementGlobalMgr::LoadRewards() reward.itemId = 0; } } + m_achievementRewards[entry] = reward; ++count; + } while (result->NextRow()); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u achievement rewards", count ); } + void AchievementGlobalMgr::LoadRewardLocales() { m_achievementRewardLocales.clear(); // need for reload case + QueryResult *result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward"); + if(!result) { barGoLink bar(1); + bar.step(); + sLog.outString(); sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty."); return; } + barGoLink bar(result->GetRowCount()); + do { Field *fields = result->Fetch(); bar.step(); + uint32 entry = fields[0].GetUInt32(); + if(m_achievementRewards.find(entry)==m_achievementRewards.end()) { sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry); continue; } + AchievementRewardLocale& data = m_achievementRewardLocales[entry]; + for(int i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[1+2*(i-1)].GetCppString(); @@ -1959,6 +2219,7 @@ void AchievementGlobalMgr::LoadRewardLocales() { if(data.subject.size() <= size_t(idx)) data.subject.resize(idx+1); + data.subject[idx] = str; } } @@ -1970,13 +2231,16 @@ void AchievementGlobalMgr::LoadRewardLocales() { if(data.text.size() <= size_t(idx)) data.text.resize(idx+1); + data.text[idx] = str; } } } } while (result->NextRow()); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() ); } |