[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / src / game / AchievementMgr.cpp
blob726316666c236e8d05a0b0cf9aa2aef6f994c150
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "AchievementMgr.h"
20 #include "Common.h"
21 #include "Player.h"
22 #include "WorldPacket.h"
23 #include "Database/DBCEnums.h"
24 #include "ObjectMgr.h"
25 #include "Guild.h"
26 #include "Database/DatabaseEnv.h"
27 #include "GameEvent.h"
28 #include "World.h"
29 #include "SpellMgr.h"
30 #include "ProgressBar.h"
32 #include "Policies/SingletonImp.h"
34 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
36 const CriteriaCastSpellRequirement AchievementGlobalMgr::m_criteriaCastSpellRequirements[CRITERIA_CAST_SPELL_REQ_COUNT] =
38 {5272, 3057, 0, 0},
39 {5273, 2784, 0, 0},
40 {5752, 9099, 0, 0},
41 {5753, 8403, 0, 0},
42 {5772, 0, 0, RACE_GNOME},
43 {5774, 0, 0, RACE_BLOODELF},
44 {5775, 0, 0, RACE_DRAENEI},
45 {5776, 0, 0, RACE_DWARF},
46 {5777, 0, 0, RACE_HUMAN},
47 {5778, 0, 0, RACE_NIGHTELF},
48 {5779, 0, 0, RACE_ORC},
49 {5780, 0, 0, RACE_TAUREN},
50 {5781, 0, 0, RACE_TROLL},
51 {5782, 0, 0, RACE_UNDEAD_PLAYER},
52 {6225, 5661, 0, 0},
53 {6226, 26044, 0, 0},
54 {6228, 739, 0, 0},
55 {6229, 927, 0, 0},
56 {6230, 1444, 0, 0},
57 {6231, 8140, 0, 0},
58 {6232, 5489, 0, 0},
59 {6233,12336, 0, 0},
60 {6234, 1351, 0, 0},
61 {6235, 5484, 0, 0},
62 {6236, 1182, 0, 0},
63 {6237, 0, CLASS_DEATH_KNIGHT, RACE_ORC},
64 {6238, 0, CLASS_WARRIOR, RACE_HUMAN},
65 {6239, 0, CLASS_SHAMAN, RACE_TAUREN},
66 {6240, 0, CLASS_DRUID, RACE_NIGHTELF},
67 {6241, 0, CLASS_ROGUE, RACE_UNDEAD_PLAYER},
68 {6242, 0, CLASS_HUNTER, RACE_TROLL},
69 {6243, 0, CLASS_MAGE, RACE_GNOME},
70 {6244, 0, CLASS_PALADIN, RACE_DWARF},
71 {6245, 0, CLASS_WARLOCK, RACE_BLOODELF},
72 {6246, 0, CLASS_PRIEST, RACE_DRAENEI},
73 {6312, 0, CLASS_WARLOCK, RACE_GNOME},
74 {6313, 0, CLASS_DEATH_KNIGHT, RACE_HUMAN},
75 {6314, 0, CLASS_PRIEST, RACE_NIGHTELF},
76 {6315, 0, CLASS_SHAMAN, RACE_ORC},
77 {6316, 0, CLASS_DRUID, RACE_TAUREN},
78 {6317, 0, CLASS_ROGUE, RACE_TROLL},
79 {6318, 0, CLASS_WARRIOR, RACE_UNDEAD_PLAYER},
80 {6319, 0, CLASS_MAGE, RACE_BLOODELF},
81 {6320, 0, CLASS_PALADIN, RACE_DRAENEI},
82 {6321, 0, CLASS_HUNTER, RACE_DWARF},
83 {6662, 31261, 0, 0}
86 AchievementMgr::AchievementMgr(Player *player)
88 m_player = player;
91 AchievementMgr::~AchievementMgr()
95 void AchievementMgr::Reset()
97 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
99 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
100 data << uint32(iter->first);
101 m_player->SendDirectMessage(&data);
104 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
106 WorldPacket data(SMSG_CRITERIA_DELETED,4);
107 data << uint32(iter->first);
108 m_player->SendDirectMessage(&data);
111 m_completedAchievements.clear();
112 m_criteriaProgress.clear();
113 DeleteFromDB(m_player->GetGUIDLow());
115 // re-fill data
116 CheckAllAchievementCriteria();
119 void AchievementMgr::DeleteFromDB(uint32 lowguid)
121 CharacterDatabase.BeginTransaction ();
122 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
123 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
124 CharacterDatabase.CommitTransaction ();
127 void AchievementMgr::SaveToDB()
129 if(!m_completedAchievements.empty())
131 bool need_execute = false;
132 std::ostringstream ssdel;
133 std::ostringstream ssins;
134 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
136 if(!iter->second.changed)
137 continue;
139 /// first new/changed record prefix
140 if(!need_execute)
142 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
143 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
144 need_execute = true;
146 /// next new/changed record prefix
147 else
149 ssdel << ", ";
150 ssins << ", ";
153 // new/changed record data
154 ssdel << iter->first;
155 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
157 /// mark as saved in db
158 iter->second.changed = false;
161 if(need_execute)
162 ssdel << ")";
164 if(need_execute)
166 CharacterDatabase.BeginTransaction ();
167 CharacterDatabase.Execute( ssdel.str().c_str() );
168 CharacterDatabase.Execute( ssins.str().c_str() );
169 CharacterDatabase.CommitTransaction ();
173 if(!m_criteriaProgress.empty())
175 /// prepare deleting and insert
176 bool need_execute_del = false;
177 bool need_execute_ins = false;
178 std::ostringstream ssdel;
179 std::ostringstream ssins;
180 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
182 if(!iter->second.changed)
183 continue;
185 // deleted data (including 0 progress state)
187 /// first new/changed record prefix (for any counter value)
188 if(!need_execute_del)
190 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
191 need_execute_del = true;
193 /// next new/changed record prefix
194 else
195 ssdel << ", ";
197 // new/changed record data
198 ssdel << iter->first;
201 // store data only for real progress
202 if(iter->second.counter != 0)
204 /// first new/changed record prefix
205 if(!need_execute_ins)
207 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
208 need_execute_ins = true;
210 /// next new/changed record prefix
211 else
212 ssins << ", ";
214 // new/changed record data
215 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
218 /// mark as updated in db
219 iter->second.changed = false;
222 if(need_execute_del) // DELETE ... IN (.... _)_
223 ssdel << ")";
225 if(need_execute_del || need_execute_ins)
227 CharacterDatabase.BeginTransaction ();
228 if(need_execute_del)
229 CharacterDatabase.Execute( ssdel.str().c_str() );
230 if(need_execute_ins)
231 CharacterDatabase.Execute( ssins.str().c_str() );
232 CharacterDatabase.CommitTransaction ();
237 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
239 if(achievementResult)
243 Field *fields = achievementResult->Fetch();
244 CompletedAchievementData& ca = m_completedAchievements[fields[0].GetUInt32()];
245 ca.date = time_t(fields[1].GetUInt64());
246 ca.changed = false;
247 } while(achievementResult->NextRow());
248 delete achievementResult;
251 if(criteriaResult)
255 Field *fields = criteriaResult->Fetch();
257 uint32 id = fields[0].GetUInt32();
258 uint32 counter = fields[1].GetUInt32();
259 time_t date = time_t(fields[2].GetUInt64());
261 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
262 if(!criteria || criteria->timeLimit && date + criteria->timeLimit < time(NULL))
263 continue;
265 CriteriaProgress& progress = m_criteriaProgress[id];
266 progress.counter = counter;
267 progress.date = date;
268 progress.changed = false;
269 } while(criteriaResult->NextRow());
270 delete criteriaResult;
275 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
277 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
279 const char *msg = "|Hplayer:$N|h[$N]|h has earned the achievement $a!";
280 if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
282 WorldPacket data(SMSG_MESSAGECHAT, 200);
283 data << uint8(CHAT_MSG_GUILD_ACHIEVEMENT);
284 data << uint32(LANG_UNIVERSAL);
285 data << uint64(GetPlayer()->GetGUID());
286 data << uint32(5);
287 data << uint64(GetPlayer()->GetGUID());
288 data << uint32(strlen(msg)+1);
289 data << msg;
290 data << uint8(0);
291 data << uint32(achievement->ID);
292 guild->BroadcastPacket(&data);
294 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
296 // broadcast realm first reached
297 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
298 data << GetPlayer()->GetName();
299 data << uint64(GetPlayer()->GetGUID());
300 data << uint32(achievement->ID);
301 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
302 sWorld.SendGlobalMessage(&data);
304 else
306 WorldPacket data(SMSG_MESSAGECHAT, 200);
307 data << uint8(CHAT_MSG_ACHIEVEMENT);
308 data << uint32(LANG_UNIVERSAL);
309 data << uint64(GetPlayer()->GetGUID());
310 data << uint32(5);
311 data << uint64(GetPlayer()->GetGUID());
312 data << uint32(strlen(msg)+1);
313 data << msg;
314 data << uint8(0);
315 data << uint32(achievement->ID);
316 GetPlayer()->SendMessageToSet(&data, true);
319 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
320 data.append(GetPlayer()->GetPackGUID());
321 data << uint32(achievement->ID);
322 data << uint32(secsToTimeBitFields(time(NULL)));
323 data << uint32(0);
324 GetPlayer()->SendMessageToSet(&data, true);
327 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
329 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
330 data << uint32(id);
332 // the counter is packed like a packed Guid
333 data.appendPackGUID(progress->counter);
335 data.append(GetPlayer()->GetPackGUID());
336 data << uint32(0);
337 data << uint32(secsToTimeBitFields(progress->date));
338 data << uint32(0); // timer 1
339 data << uint32(0); // timer 2
340 GetPlayer()->SendMessageToSet(&data, true);
344 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
346 void AchievementMgr::CheckAllAchievementCriteria()
348 // suppress sending packets
349 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
350 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
354 * this function will be called whenever the user might have done a criteria relevant action
356 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
358 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
360 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
361 return;
363 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
364 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
366 AchievementCriteriaEntry const *achievementCriteria = (*i);
368 // don't update already completed criteria
369 if(IsCompletedCriteria(achievementCriteria))
370 continue;
372 if(achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
373 continue;
375 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
376 if(!achievement)
377 continue;
379 if(achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE ||
380 achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)
381 continue;
383 switch (type)
385 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
386 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
387 break;
388 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
389 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1);
390 break;
391 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
392 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
393 if(!miscvalue1)
394 continue;
395 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
396 continue;
397 SetCriteriaProgress(achievementCriteria, miscvalue2, true);
398 break;
399 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
400 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
401 SetCriteriaProgress(achievementCriteria, skillvalue);
402 break;
403 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
405 uint32 counter =0;
406 for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
407 if(itr->second.m_rewarded)
408 counter++;
409 SetCriteriaProgress(achievementCriteria, counter);
410 break;
412 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
414 uint32 counter =0;
415 for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
417 Quest const* quest = objmgr.GetQuestTemplate(itr->first);
418 if(itr->second.m_rewarded && quest->GetZoneOrSort() == achievementCriteria->complete_quests_in_zone.zoneID)
419 counter++;
421 SetCriteriaProgress(achievementCriteria, counter);
422 break;
424 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
425 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
426 if(!miscvalue1)
427 continue;
428 SetCriteriaProgress(achievementCriteria, miscvalue1, true);
429 break;
430 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
431 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
432 if(!miscvalue1)
433 continue;
434 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
435 continue;
436 SetCriteriaProgress(achievementCriteria, miscvalue1, true);
437 break;
438 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
439 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
440 SetCriteriaProgress(achievementCriteria, 1);
441 break;
442 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
443 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
444 SetCriteriaProgress(achievementCriteria, 1);
445 break;
446 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
447 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
448 if(!miscvalue1)
449 continue;
450 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
451 continue;
452 SetCriteriaProgress(achievementCriteria, 1, true);
453 break;
454 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
455 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
456 if(!miscvalue1)
457 continue;
458 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
459 continue;
460 SetCriteriaProgress(achievementCriteria, 1, true);
461 break;
462 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
463 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
464 if(!miscvalue1)
465 continue;
466 SetCriteriaProgress(achievementCriteria, 1, true);
467 break;
468 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
470 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
471 if(!miscvalue1)
472 continue;
473 if(achievement->ID == 1260)
475 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED)
476 continue;
477 // TODO: hardcoding eventid is bad, it can differ from DB to DB - maye implement something using HolidayNames.dbc?
478 if(!gameeventmgr.IsActiveEvent(26))
479 continue;
481 // miscvalue1 is the ingame fallheight*100 as stored in dbc
482 SetCriteriaProgress(achievementCriteria, miscvalue1);
483 break;
485 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
486 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
487 SetCriteriaProgress(achievementCriteria, 1);
488 break;
489 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
490 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
491 if(!miscvalue1)
492 continue;
493 if(achievementCriteria->use_item.itemID != miscvalue1)
494 continue;
495 SetCriteriaProgress(achievementCriteria, 1, true);
496 break;
497 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
498 // speedup for non-login case
499 if(miscvalue1 && achievementCriteria->own_item.itemID!=miscvalue1)
500 continue;
501 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
502 break;
503 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
504 // You _have_ to loot that item, just owning it when logging in does _not_ count!
505 if(!miscvalue1)
506 continue;
507 if(miscvalue1 != achievementCriteria->own_item.itemID)
508 continue;
509 SetCriteriaProgress(achievementCriteria, miscvalue2, true);
510 break;
511 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
512 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
513 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
514 continue;
515 SetCriteriaProgress(achievementCriteria, 1, true);
516 break;
517 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
518 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
519 continue;
520 SetCriteriaProgress(achievementCriteria, 1, true);
521 break;
522 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
524 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
525 continue;
527 // those requirements couldn't be found in the dbc
528 if (CriteriaCastSpellRequirement const* requirement = AchievementGlobalMgr::GetCriteriaCastSpellRequirement(achievementCriteria))
530 if (!unit)
531 continue;
533 if (requirement->creatureEntry && unit->GetEntry() != requirement->creatureEntry)
534 continue;
536 if (requirement->playerRace && (unit->GetTypeId() != TYPEID_PLAYER || unit->getRace()!=requirement->playerRace))
537 continue;
539 if (requirement->playerClass && (unit->GetTypeId() != TYPEID_PLAYER || unit->getClass()!=requirement->playerClass))
540 continue;
543 SetCriteriaProgress(achievementCriteria, 1, true);
544 break;
546 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
548 uint32 spellCount = 0;
549 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
550 spellIter != GetPlayer()->GetSpellMap().end();
551 spellIter++)
553 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
554 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
555 skillIter++)
557 if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine)
558 spellCount++;
561 SetCriteriaProgress(achievementCriteria, spellCount);
562 break;
564 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
566 // skip for login case
567 if(!miscvalue1)
568 continue;
569 SetCriteriaProgress(achievementCriteria, 1);
570 break;
572 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
574 int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID);
575 if (reputation > 0)
576 SetCriteriaProgress(achievementCriteria, reputation);
577 break;
579 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
581 uint32 counter = 0;
582 const FactionStateList factionStateList = GetPlayer()->GetFactionStateList();
583 for (FactionStateList::const_iterator iter = factionStateList.begin(); iter!= factionStateList.end(); iter++)
585 if(GetPlayer()->ReputationToRank(iter->second.Standing) >= REP_EXALTED)
586 ++counter;
588 SetCriteriaProgress(achievementCriteria, counter);
589 break;
591 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
593 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
594 if(!worldOverlayEntry)
595 break;
597 int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID);
598 if(exploreFlag < 0)
599 break;
601 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
602 uint32 mask = 1<< (uint32(exploreFlag) % 32);
604 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
605 SetCriteriaProgress(achievementCriteria, 1);
606 break;
608 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
609 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
611 // miscvalue1 = itemid
612 // miscvalue2 = diced value
613 if(!miscvalue1)
614 continue;
615 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
616 continue;
617 ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
619 uint32 requiredItemLevel = 0;
620 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
621 requiredItemLevel = 185;
623 if(!pProto || pProto->ItemLevel <requiredItemLevel)
624 continue;
625 SetCriteriaProgress(achievementCriteria, 1, true);
626 break;
629 if(IsCompletedCriteria(achievementCriteria))
630 CompletedCriteria(achievementCriteria);
634 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria)
636 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
637 if(!achievement)
638 return false;
640 // counter can never complete
641 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
642 return false;
644 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
646 // someone on this realm has already completed that achievement
647 if(achievementmgr.IsRealmCompleted(achievement))
648 return false;
651 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
652 if(itr == m_criteriaProgress.end())
653 return false;
655 CriteriaProgress const* progress = &itr->second;
657 switch(achievementCriteria->requiredType)
659 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
660 if(achievement->ID == 467 && GetPlayer()->getClass() != CLASS_SHAMAN ||
661 achievement->ID == 466 && GetPlayer()->getClass() != CLASS_DRUID ||
662 achievement->ID == 465 && GetPlayer()->getClass() != CLASS_PALADIN ||
663 achievement->ID == 464 && GetPlayer()->getClass() != CLASS_PRIEST ||
664 achievement->ID == 463 && GetPlayer()->getClass() != CLASS_WARLOCK ||
665 achievement->ID == 462 && GetPlayer()->getClass() != CLASS_HUNTER ||
666 achievement->ID == 461 && GetPlayer()->getClass() != CLASS_DEATH_KNIGHT ||
667 achievement->ID == 460 && GetPlayer()->getClass() != CLASS_MAGE ||
668 achievement->ID == 459 && GetPlayer()->getClass() != CLASS_WARRIOR ||
669 achievement->ID == 458 && GetPlayer()->getClass() != CLASS_ROGUE ||
671 achievement->ID == 1404 && GetPlayer()->getRace() != RACE_GNOME ||
672 achievement->ID == 1405 && GetPlayer()->getRace() != RACE_BLOODELF ||
673 achievement->ID == 1406 && GetPlayer()->getRace() != RACE_DRAENEI ||
674 achievement->ID == 1407 && GetPlayer()->getRace() != RACE_DWARF ||
675 achievement->ID == 1408 && GetPlayer()->getRace() != RACE_HUMAN ||
676 achievement->ID == 1409 && GetPlayer()->getRace() != RACE_NIGHTELF ||
677 achievement->ID == 1410 && GetPlayer()->getRace() != RACE_ORC ||
678 achievement->ID == 1411 && GetPlayer()->getRace() != RACE_TAUREN ||
679 achievement->ID == 1412 && GetPlayer()->getRace() != RACE_TROLL ||
680 achievement->ID == 1413 && GetPlayer()->getRace() != RACE_UNDEAD_PLAYER )
681 return false;
682 return progress->counter >= achievementCriteria->reach_level.level;
683 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
684 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
685 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
686 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
687 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
688 return progress->counter >= 1;
689 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
690 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
691 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
692 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
693 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
694 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
695 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
696 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
697 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
698 return progress->counter >= 1;
699 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
700 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
701 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
702 return progress->counter >= 1;
703 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
704 return progress->counter >= achievementCriteria->use_item.itemCount;
705 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
706 return progress->counter >= achievementCriteria->own_item.itemCount;
707 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
708 return progress->counter >= achievementCriteria->loot_item.itemCount;
709 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
710 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
711 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
712 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
713 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
714 return progress->counter >= achievementCriteria->cast_spell.castCount;
715 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
716 return progress->counter >= achievementCriteria->learn_skilline_spell.spellCount;
717 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
718 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
719 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
720 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
721 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
722 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
723 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
724 return progress->counter >= 1;
725 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
726 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
727 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
729 // handle all statistic-only criteria here
730 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
731 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
732 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
733 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
734 return false;
736 return false;
739 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry const* criteria)
741 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
742 if(!achievement)
743 return;
744 // counter can never complete
745 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
746 return;
748 if(criteria->completionFlag & ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL || GetAchievementCompletionState(achievement)==ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED)
750 CompletedAchievement(achievement);
754 // TODO: achievement 705 requires 4 criteria to be fulfilled
755 AchievementCompletionState AchievementMgr::GetAchievementCompletionState(AchievementEntry const* entry)
757 if(m_completedAchievements.find(entry->ID)!=m_completedAchievements.end())
758 return ACHIEVEMENT_COMPLETED_COMPLETED_STORED;
760 bool foundOutstanding = false;
761 for (uint32 entryId = 0; entryId<sAchievementCriteriaStore.GetNumRows(); entryId++)
763 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
764 if(!criteria || criteria->referredAchievement!= entry->ID)
765 continue;
767 if(IsCompletedCriteria(criteria) && criteria->completionFlag & ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL)
768 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED;
770 // found an umcompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
771 if(!IsCompletedCriteria(criteria))
772 foundOutstanding = true;
774 if(foundOutstanding)
775 return ACHIEVEMENT_COMPLETED_NONE;
776 else
777 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED;
780 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 newValue, bool relative)
782 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u)", entry->ID, newValue);
783 CriteriaProgress *progress = NULL;
785 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
787 if(iter == m_criteriaProgress.end())
789 // not create record for 0 counter
790 if(newValue == 0)
791 return;
793 progress = &m_criteriaProgress[entry->ID];
794 progress->counter = newValue;
795 progress->date = time(NULL);
797 else
799 progress = &iter->second;
800 if(relative)
801 newValue += progress->counter;
803 // not update (not mark as changed) if counter will have same value
804 if(progress->counter == newValue)
805 return;
807 progress->counter = newValue;
810 progress->changed = true;
812 if(entry->timeLimit)
814 time_t now = time(NULL);
815 if(progress->date + entry->timeLimit < now)
817 progress->counter = 1;
819 // also it seems illogical, the timeframe will be extended at every criteria update
820 progress->date = now;
822 SendCriteriaUpdate(entry->ID,progress);
825 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
827 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
828 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
829 return;
831 SendAchievementEarned(achievement);
832 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
833 ca.date = time(NULL);
834 ca.changed = true;
836 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
837 // TODO: where do set this instead?
838 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
839 achievementmgr.SetRealmCompleted(achievement);
841 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
843 // reward items and titles if any
844 AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
846 // no rewards
847 if(!reward)
848 return;
850 // titles
851 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE?0:1])
853 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
854 GetPlayer()->SetTitle(titleEntry);
857 // mail
858 if(reward->sender)
860 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
862 MailItemsInfo mi;
863 if(item)
865 // save new item before send
866 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
868 // item
869 mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
872 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
874 // subject and text
875 std::string subject = reward->subject;
876 std::string text = reward->text;
877 if ( loc_idx >= 0 )
879 if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement))
881 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
882 subject = loc->subject[loc_idx];
883 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
884 text = loc->text[loc_idx];
888 uint32 itemTextId = objmgr.CreateItemText( text );
890 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
894 void AchievementMgr::SendAllAchievementData()
896 // since we don't know the exact size of the packed GUIDs this is just an approximation
897 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
898 BuildAllDataPacket(&data);
899 GetPlayer()->GetSession()->SendPacket(&data);
902 void AchievementMgr::SendRespondInspectAchievements(Player* player)
904 // since we don't know the exact size of the packed GUIDs this is just an approximation
905 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
906 data.append(GetPlayer()->GetPackGUID());
907 BuildAllDataPacket(&data);
908 player->GetSession()->SendPacket(&data);
912 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
914 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
916 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
918 *data << uint32(iter->first);
919 *data << uint32(secsToTimeBitFields(iter->second.date));
921 *data << int32(-1);
923 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
925 *data << uint32(iter->first);
926 data->appendPackGUID(iter->second.counter);
927 data->append(GetPlayer()->GetPackGUID());
928 *data << uint32(0);
929 *data << uint32(secsToTimeBitFields(iter->second.date));
930 *data << uint32(0);
931 *data << uint32(0);
934 *data << int32(-1);
937 //==========================================================
938 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
940 return m_AchievementCriteriasByType[type];
943 void AchievementGlobalMgr::LoadAchievementCriteriaList()
945 if(sAchievementCriteriaStore.GetNumRows()==0)
947 barGoLink bar(1);
948 bar.step();
950 sLog.outString("");
951 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
952 return;
955 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
956 for (uint32 entryId = 0; entryId<sAchievementCriteriaStore.GetNumRows(); entryId++)
958 bar.step();
960 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
961 if(!criteria)
962 continue;
964 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
967 sLog.outString();
968 sLog.outString(">> Loaded %u achievement criteria.",m_AchievementCriteriasByType->size());
972 void AchievementGlobalMgr::LoadCompletedAchievements()
974 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
976 if(!result)
978 barGoLink bar(1);
979 bar.step();
981 sLog.outString("");
982 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
983 return;
986 barGoLink bar(result->GetRowCount());
989 bar.step();
990 Field *fields = result->Fetch();
991 m_allCompletedAchievements.insert(fields[0].GetUInt32());
992 } while(result->NextRow());
994 delete result;
996 sLog.outString("");
997 sLog.outString(">> Loaded %u realm completed achievements.",m_allCompletedAchievements.size());
1000 void AchievementGlobalMgr::LoadRewards()
1002 m_achievementRewards.clear(); // need for reload case
1004 // 0 1 2 3 4 5 6
1005 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1007 if(!result)
1009 barGoLink bar(1);
1011 bar.step();
1013 sLog.outString("");
1014 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1015 return;
1018 barGoLink bar(result->GetRowCount());
1022 bar.step();
1024 Field *fields = result->Fetch();
1025 uint32 entry = fields[0].GetUInt32();
1026 if (!sAchievementStore.LookupEntry(entry))
1028 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
1029 continue;
1032 AchievementReward reward;
1033 reward.titleId[0] = fields[1].GetUInt32();
1034 reward.titleId[1] = fields[2].GetUInt32();
1035 reward.itemId = fields[3].GetUInt32();
1036 reward.sender = fields[4].GetUInt32();
1037 reward.subject = fields[5].GetCppString();
1038 reward.text = fields[6].GetCppString();
1040 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
1041 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]);
1043 // must be title or mail at least
1044 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
1046 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
1047 continue;
1050 if (reward.titleId[0])
1052 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
1053 if (!titleEntry)
1055 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
1056 reward.titleId[0] = 0;
1060 if (reward.titleId[1])
1062 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
1063 if (!titleEntry)
1065 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
1066 reward.titleId[1] = 0;
1070 //check mail data before item for report including wrong item case
1071 if (reward.sender)
1073 if (!objmgr.GetCreatureTemplate(reward.sender))
1075 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
1076 reward.sender = 0;
1079 else
1081 if (reward.itemId)
1082 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
1084 if (!reward.subject.empty())
1085 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
1087 if (!reward.text.empty())
1088 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
1091 if (reward.itemId)
1093 if (!objmgr.GetItemPrototype(reward.itemId))
1095 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
1096 reward.itemId = 0;
1100 m_achievementRewards[entry] = reward;
1102 } while (result->NextRow());
1104 delete result;
1106 sLog.outString();
1107 sLog.outString( ">> Loaded %u achievement reward locale strings", m_achievementRewardLocales.size() );
1110 void AchievementGlobalMgr::LoadRewardLocales()
1112 m_achievementRewardLocales.clear(); // need for reload case
1114 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");
1116 if(!result)
1118 barGoLink bar(1);
1120 bar.step();
1122 sLog.outString("");
1123 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1124 return;
1127 barGoLink bar(result->GetRowCount());
1131 Field *fields = result->Fetch();
1132 bar.step();
1134 uint32 entry = fields[0].GetUInt32();
1136 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
1138 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
1139 continue;
1142 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
1144 for(int i = 1; i < MAX_LOCALE; ++i)
1146 std::string str = fields[1+2*(i-1)].GetCppString();
1147 if(!str.empty())
1149 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
1150 if(idx >= 0)
1152 if(data.subject.size() <= idx)
1153 data.subject.resize(idx+1);
1155 data.subject[idx] = str;
1158 str = fields[1+2*(i-1)+1].GetCppString();
1159 if(!str.empty())
1161 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
1162 if(idx >= 0)
1164 if(data.text.size() <= idx)
1165 data.text.resize(idx+1);
1167 data.text[idx] = str;
1171 } while (result->NextRow());
1173 delete result;
1175 sLog.outString();
1176 sLog.outString( ">> Loaded %u achievement reward locale strings", m_achievementRewardLocales.size() );