TrinityCore
AchievementMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "AchievementMgr.h"
19#include "AchievementPackets.h"
20#include "DB2HotfixGenerator.h"
21#include "DB2Stores.h"
22#include "CellImpl.h"
23#include "ChatTextBuilder.h"
24#include "Containers.h"
25#include "DatabaseEnv.h"
26#include "GameTime.h"
27#include "GridNotifiersImpl.h"
28#include "Group.h"
29#include "Guild.h"
30#include "GuildMgr.h"
31#include "Item.h"
32#include "Language.h"
33#include "Log.h"
34#include "Mail.h"
35#include "ObjectMgr.h"
36#include "RBAC.h"
37#include "StringConvert.h"
38#include "ScriptMgr.h"
39#include "World.h"
40#include "WorldSession.h"
41#include <sstream>
42
44{
45 AchievementEntry const* operator()(std::pair<uint32 const, CompletedAchievementData> const& val)
46 {
47 AchievementEntry const* achievement = sAchievementStore.LookupEntry(val.first);
48 if (achievement && !(achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN))
49 return achievement;
50 return nullptr;
51 }
52};
53
54AchievementMgr::AchievementMgr() : _achievementPoints(0) { }
55
57
62{
63 // suppress sending packets
64 for (uint32 i = 0; i < uint32(CriteriaType::Count); ++i)
65 UpdateCriteria(CriteriaType(i), 0, 0, 0, nullptr, referencePlayer);
66}
67
68bool AchievementMgr::HasAchieved(uint32 achievementId) const
69{
70 return _completedAchievements.find(achievementId) != _completedAchievements.end();
71}
72
74{
75 return _achievementPoints;
76}
77
79{
80 std::vector<uint32> achievementIds;
81 std::transform(_completedAchievements.begin(), _completedAchievements.end(), std::back_inserter(achievementIds), [](std::pair<uint32 const, CompletedAchievementData> const& achievement)
82 {
83 return achievement.first;
84 });
85 return achievementIds;
86}
87
88bool AchievementMgr::CanUpdateCriteriaTree(Criteria const* criteria, CriteriaTree const* tree, Player* referencePlayer) const
89{
90 AchievementEntry const* achievement = tree->Achievement;
91 if (!achievement)
92 return false;
93
94 if (HasAchieved(achievement->ID))
95 {
96 TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: {} Type {} Achievement {}) Achievement already earned",
97 criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), achievement->ID);
98 return false;
99 }
100
101 if ((achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) ||
102 (achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE))
103 {
104 TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: {} Type {} Achievement {}) Wrong faction",
105 criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), achievement->ID);
106 return false;
107 }
108
109 // Don't update realm first achievements if the player's account isn't allowed to do so
112 return false;
113
114 if (achievement->CovenantID && referencePlayer->m_playerData->CovenantID != achievement->CovenantID)
115 {
116 TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: {} Type {} Achievement {}) Wrong covenant",
117 criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), achievement->ID);
118 return false;
119 }
120
121 return CriteriaHandler::CanUpdateCriteriaTree(criteria, tree, referencePlayer);
122}
123
125{
126 AchievementEntry const* achievement = tree->Achievement;
127 if (!achievement)
128 return false;
129
130 // counter can never complete
131 if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER)
132 return false;
133
135 {
136 // someone on this realm has already completed that achievement
137 if (sAchievementMgr->IsRealmCompleted(achievement))
138 return false;
139 }
140
141 return true;
142}
143
145{
146 AchievementEntry const* achievement = tree->Achievement;
147 if (!achievement)
148 return;
149
150 // counter can never complete
151 if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER)
152 return;
153
154 // already completed and stored
155 if (HasAchieved(achievement->ID))
156 return;
157
158 if (IsCompletedAchievement(achievement))
159 CompletedAchievement(achievement, referencePlayer);
160}
161
163{
164 AchievementEntry const* achievement = tree->Achievement;
165 if (!achievement)
166 return;
167
168 // check again the completeness for SUMM and REQ COUNT achievements,
169 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
170 if (achievement->Flags & ACHIEVEMENT_FLAG_SUMM)
171 if (IsCompletedAchievement(achievement))
172 CompletedAchievement(achievement, referencePlayer);
173
174 if (std::vector<AchievementEntry const*> const* achRefList = sAchievementMgr->GetAchievementByReferencedId(achievement->ID))
175 for (AchievementEntry const* refAchievement : *achRefList)
176 if (IsCompletedAchievement(refAchievement))
177 CompletedAchievement(refAchievement, referencePlayer);
178}
179
181{
182 // counter can never complete
183 if (entry->Flags & ACHIEVEMENT_FLAG_COUNTER)
184 return false;
185
186 CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(entry->CriteriaTree);
187 if (!tree)
188 return false;
189
190 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
191 // Oddly, the target count is NOT contained in the achievement, but in each individual criteria
192 if (entry->Flags & ACHIEVEMENT_FLAG_SUMM)
193 {
194 int64 progress = 0;
195 CriteriaMgr::WalkCriteriaTree(tree, [this, &progress](CriteriaTree const* criteriaTree)
196 {
197 if (criteriaTree->Criteria)
198 if (CriteriaProgress const* criteriaProgress = this->GetCriteriaProgress(criteriaTree->Criteria))
199 progress += criteriaProgress->Counter;
200 });
201 return progress >= tree->Entry->Amount;
202 }
203
204 return IsCompletedCriteriaTree(tree);
205}
206
208{
209 return HasAchieved(achievementId);
210}
211
213{
214}
215
217{
219
220 for (std::pair<uint32 const, CompletedAchievementData> const& completedAchievement : _completedAchievements)
221 {
223 achievementDeleted.AchievementID = completedAchievement.first;
224 SendPacket(achievementDeleted.Write());
225 }
226
230
231 // re-fill data
233}
234
236{
237 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
238
240 stmt->setUInt64(0, guid.GetCounter());
241 trans->Append(stmt);
242
243 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS);
244 stmt->setUInt64(0, guid.GetCounter());
245 trans->Append(stmt);
246
247 CharacterDatabase.CommitTransaction(trans);
248}
249
251{
252 if (achievementResult)
253 {
254 do
255 {
256 Field* fields = achievementResult->Fetch();
257 uint32 achievementid = fields[0].GetUInt32();
258
259 // must not happen: cleanup at server startup in sAchievementMgr->LoadCompletedAchievements()
260 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementid);
261 if (!achievement)
262 continue;
263
265 ca.Date = fields[1].GetInt64();
266 ca.Changed = false;
267
268 _achievementPoints += achievement->Points;
269
270 // title achievement rewards are retroactive
271 if (AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement))
272 if (uint32 titleId = reward->TitleId[Player::TeamForRace(_owner->GetRace()) == ALLIANCE ? 0 : 1])
273 if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
274 _owner->SetTitle(titleEntry);
275
276 } while (achievementResult->NextRow());
277 }
278
279 if (criteriaResult)
280 {
281 time_t now = GameTime::GetGameTime();
282 do
283 {
284 Field* fields = criteriaResult->Fetch();
285 uint32 id = fields[0].GetUInt32();
286 uint64 counter = fields[1].GetUInt64();
287 time_t date = fields[2].GetInt64();
288
289 Criteria const* criteria = sCriteriaMgr->GetCriteria(id);
290 if (!criteria)
291 {
292 // Removing non-existing criteria data for all characters
293 TC_LOG_ERROR("criteria.achievement", "Non-existing achievement criteria {} data has been removed from the table `character_achievement_progress`.", id);
294
296 stmt->setUInt32(0, id);
297 CharacterDatabase.Execute(stmt);
298
299 continue;
300 }
301
302 if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now)
303 continue;
304
305 CriteriaProgress& progress = _criteriaProgress[id];
306 progress.Counter = counter;
307 progress.Date = date;
308 progress.PlayerGUID = _owner->GetGUID();
309 progress.Changed = false;
310 } while (criteriaResult->NextRow());
311 }
312}
313
315{
316 if (!_completedAchievements.empty())
317 {
318 for (std::pair<uint32 const, CompletedAchievementData>& completedAchievement : _completedAchievements)
319 {
320 if (!completedAchievement.second.Changed)
321 continue;
322
324 stmt->setUInt32(0, completedAchievement.first);
325 stmt->setUInt64(1, _owner->GetGUID().GetCounter());
326 trans->Append(stmt);
327
328 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT);
329 stmt->setUInt64(0, _owner->GetGUID().GetCounter());
330 stmt->setUInt32(1, completedAchievement.first);
331 stmt->setInt64(2, completedAchievement.second.Date);
332 trans->Append(stmt);
333
334 completedAchievement.second.Changed = false;
335 }
336 }
337
338 if (!_criteriaProgress.empty())
339 {
340 for (std::pair<uint32 const, CriteriaProgress>& criteriaProgres : _criteriaProgress)
341 {
342 if (!criteriaProgres.second.Changed)
343 continue;
344
346 stmt->setUInt64(0, _owner->GetGUID().GetCounter());
347 stmt->setUInt32(1, criteriaProgres.first);
348 trans->Append(stmt);
349
350 if (criteriaProgres.second.Counter)
351 {
352 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS);
353 stmt->setUInt64(0, _owner->GetGUID().GetCounter());
354 stmt->setUInt32(1, criteriaProgres.first);
355 stmt->setUInt64(2, criteriaProgres.second.Counter);
356 stmt->setInt64(3, criteriaProgres.second.Date);
357 trans->Append(stmt);
358 }
359
360 criteriaProgres.second.Changed = false;
361 }
362 }
363}
364
365void PlayerAchievementMgr::SendAllData(Player const* /*receiver*/) const
366{
367 VisibleAchievementCheck filterInvisible;
369
371 achievementData.Data.Earned.reserve(_completedAchievements.size());
372 achievementData.Data.Progress.reserve(_criteriaProgress.size());
373
374 for (std::pair<uint32 const, CompletedAchievementData> const& completedAchievement : _completedAchievements)
375 {
376 AchievementEntry const* achievement = filterInvisible(completedAchievement);
377 if (!achievement)
378 continue;
379
380 WorldPackets::Achievement::EarnedAchievement& earned = achievementData.Data.Earned.emplace_back();
381 earned.Id = completedAchievement.first;
382 earned.Date.SetUtcTimeFromUnixTime(completedAchievement.second.Date);
383 earned.Date += _owner->GetSession()->GetTimezoneOffset();
384 if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT))
385 {
386 earned.Owner = _owner->GetGUID();
388 }
389 }
390
391 for (std::pair<uint32 const, CriteriaProgress> const& criteriaProgres : _criteriaProgress)
392 {
393 Criteria const* criteria = sCriteriaMgr->GetCriteria(criteriaProgres.first);
394
395 WorldPackets::Achievement::CriteriaProgress& progress = achievementData.Data.Progress.emplace_back();
396 progress.Id = criteriaProgres.first;
397 progress.Quantity = criteriaProgres.second.Counter;
398 progress.Player = criteriaProgres.second.PlayerGUID;
399 progress.Flags = 0;
400 progress.Date.SetUtcTimeFromUnixTime(criteriaProgres.second.Date);
401 progress.Date += _owner->GetSession()->GetTimezoneOffset();
402 progress.TimeFromStart = Seconds::zero();
403 progress.TimeFromCreate = Seconds::zero();
404
405 if (criteria->FlagsCu & CRITERIA_FLAG_CU_ACCOUNT)
406 {
407 WorldPackets::Achievement::CriteriaProgress& accountProgress = allAccountCriteria.Progress.emplace_back();
408 accountProgress.Id = criteriaProgres.first;
409 accountProgress.Quantity = criteriaProgres.second.Counter;
410 accountProgress.Player = _owner->GetSession()->GetBattlenetAccountGUID();
411 accountProgress.Flags = 0;
412 accountProgress.Date.SetUtcTimeFromUnixTime(criteriaProgres.second.Date);
413 accountProgress.Date += _owner->GetSession()->GetTimezoneOffset();
414 accountProgress.TimeFromStart = Seconds::zero();
415 accountProgress.TimeFromCreate = Seconds::zero();
416 }
417 }
418
419 if (!allAccountCriteria.Progress.empty())
420 SendPacket(allAccountCriteria.Write());
421
422 SendPacket(achievementData.Write());
423}
424
425void PlayerAchievementMgr::SendAchievementInfo(Player* receiver, uint32 /*achievementId = 0 */) const
426{
427 VisibleAchievementCheck filterInvisible;
429 inspectedAchievements.Player = _owner->GetGUID();
430 inspectedAchievements.Data.Earned.reserve(_completedAchievements.size());
431 inspectedAchievements.Data.Progress.reserve(_criteriaProgress.size());
432
433 for (std::pair<uint32 const, CompletedAchievementData> const& completedAchievement : _completedAchievements)
434 {
435 AchievementEntry const* achievement = filterInvisible(completedAchievement);
436 if (!achievement)
437 continue;
438
439 WorldPackets::Achievement::EarnedAchievement& earned = inspectedAchievements.Data.Earned.emplace_back();
440 earned.Id = completedAchievement.first;
441 earned.Date.SetUtcTimeFromUnixTime(completedAchievement.second.Date);
442 earned.Date += receiver->GetSession()->GetTimezoneOffset();
443 if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT))
444 {
445 earned.Owner = _owner->GetGUID();
447 }
448 }
449
450 for (std::pair<uint32 const, CriteriaProgress> const& criteriaProgres : _criteriaProgress)
451 {
452 WorldPackets::Achievement::CriteriaProgress& progress = inspectedAchievements.Data.Progress.emplace_back();
453 progress.Id = criteriaProgres.first;
454 progress.Quantity = criteriaProgres.second.Counter;
455 progress.Player = criteriaProgres.second.PlayerGUID;
456 progress.Flags = 0;
457 progress.Date.SetUtcTimeFromUnixTime(criteriaProgres.second.Date);
458 progress.Date += receiver->GetSession()->GetTimezoneOffset();
459 progress.TimeFromStart = Seconds::zero();
460 progress.TimeFromCreate = Seconds::zero();
461 }
462
463 receiver->SendDirectMessage(inspectedAchievements.Write());
464}
465
467{
468 // Disable for GameMasters with GM-mode enabled or for players that don't have the related RBAC permission
470 return;
471
472 if ((achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) ||
473 (achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE))
474 return;
475
476 if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID))
477 return;
478
480 if (Guild* guild = referencePlayer->GetGuild())
481 guild->AddGuildNews(GUILD_NEWS_PLAYER_ACHIEVEMENT, referencePlayer->GetGUID(), achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID);
482
484 SendAchievementEarned(achievement);
485
486 TC_LOG_INFO("criteria.achievement", "PlayerAchievementMgr::CompletedAchievement({}). {}", achievement->ID, GetOwnerInfo());
487
490 ca.Changed = true;
491
493 sAchievementMgr->SetRealmCompleted(achievement);
494
495 if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG))
496 _achievementPoints += achievement->Points;
497
498 referencePlayer->UpdateCriteria(CriteriaType::EarnAchievement, achievement->ID, 0, 0, nullptr);
499 referencePlayer->UpdateCriteria(CriteriaType::EarnAchievementPoints, achievement->Points, 0, 0, nullptr);
500
501 sScriptMgr->OnAchievementCompleted(referencePlayer, achievement);
502
503 // reward items and titles if any
504 AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement);
505
506 // no rewards
507 if (!reward)
508 return;
509
510 // titles
515 if (uint32 titleId = reward->TitleId[achievement->ID == 1793 ? _owner->GetNativeGender() : (_owner->GetTeam() == ALLIANCE ? 0 : 1)])
516 if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
517 _owner->SetTitle(titleEntry);
518
519 // mail
520 if (reward->SenderCreatureId)
521 {
522 MailDraft draft(uint16(reward->MailTemplateId));
523
524 if (!reward->MailTemplateId)
525 {
526 // subject and text
527 std::string subject = reward->Subject;
528 std::string text = reward->Body;
529
531 if (localeConstant != LOCALE_enUS)
532 {
533 if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement))
534 {
535 ObjectMgr::GetLocaleString(loc->Subject, localeConstant, subject);
536 ObjectMgr::GetLocaleString(loc->Body, localeConstant, text);
537 }
538 }
539
540 draft = MailDraft(subject, text);
541 }
542
543 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
544
545 Item* item = reward->ItemId ? Item::CreateItem(reward->ItemId, 1, ItemContext::NONE, _owner) : nullptr;
546 if (item)
547 {
548 // save new item before send
549 item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted
550
551 // item
552 draft.AddItem(item);
553 }
554
556 CharacterDatabase.CommitTransaction(trans);
557 }
558}
559
561{
562 if (ModifierTreeNode const* modifierTree = sCriteriaMgr->GetModifierTree(modifierTreeId))
563 return ModifierTreeSatisfied(modifierTree, 0, 0, nullptr, _owner);
564
565 return false;
566}
567
568void PlayerAchievementMgr::SendCriteriaUpdate(Criteria const* criteria, CriteriaProgress const* progress, Seconds timeElapsed, bool timedCompleted) const
569{
570 if (criteria->FlagsCu & CRITERIA_FLAG_CU_ACCOUNT)
571 {
573
574 criteriaUpdate.Progress.Id = criteria->ID;
575 criteriaUpdate.Progress.Quantity = progress->Counter;
577 criteriaUpdate.Progress.Flags = 0;
578 if (criteria->Entry->StartTimer)
579 criteriaUpdate.Progress.Flags = timedCompleted ? 1 : 0; // 1 is for keeping the counter at 0 in client
580
581 criteriaUpdate.Progress.Date.SetUtcTimeFromUnixTime(progress->Date);
582 criteriaUpdate.Progress.Date += _owner->GetSession()->GetTimezoneOffset();
583 criteriaUpdate.Progress.TimeFromStart = timeElapsed;
584 criteriaUpdate.Progress.TimeFromCreate = Seconds::zero();
585
586 SendPacket(criteriaUpdate.Write());
587 }
588 if (criteria->FlagsCu & CRITERIA_FLAG_CU_PLAYER)
589 {
591
592 criteriaUpdate.CriteriaID = criteria->ID;
593 criteriaUpdate.Quantity = progress->Counter;
594 criteriaUpdate.PlayerGUID = _owner->GetGUID();
595 criteriaUpdate.Flags = 0;
596 if (criteria->Entry->StartTimer)
597 criteriaUpdate.Flags = timedCompleted ? 1 : 0; // 1 is for keeping the counter at 0 in client
598
599 criteriaUpdate.CurrentTime.SetUtcTimeFromUnixTime(progress->Date);
600 criteriaUpdate.CurrentTime += _owner->GetSession()->GetTimezoneOffset();
601 criteriaUpdate.ElapsedTime = timeElapsed;
602 criteriaUpdate.CreationTime = 0;
603
604 SendPacket(criteriaUpdate.Write());
605 }
606}
607
609{
611 criteriaDeleted.CriteriaID = criteriaId;
612 SendPacket(criteriaDeleted.Write());
613}
614
616{
617 // Don't send for achievements with ACHIEVEMENT_FLAG_HIDDEN
618 if (achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN)
619 return;
620
621 TC_LOG_DEBUG("criteria.achievement", "PlayerAchievementMgr::SendAchievementEarned({})", achievement->ID);
622
623 if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG))
624 {
625 if (Guild* guild = sGuildMgr->GetGuildById(_owner->GetGuildId()))
626 {
629 guild->BroadcastWorker(_localizer, _owner);
630 }
631
633 {
634 // broadcast realm first reached
636 serverFirstAchievement.Name = _owner->GetName();
637 serverFirstAchievement.PlayerGUID = _owner->GetGUID();
638 serverFirstAchievement.AchievementID = achievement->ID;
639 sWorld->SendGlobalMessage(serverFirstAchievement.Write());
640 }
641 // if player is in world he can tell his friends about new achievement
642 else if (_owner->IsInWorld())
643 {
648 }
649 }
650
651 auto achievementEarnedBuilder = [&](Player const* receiver)
652 {
654 achievementEarned.Sender = _owner->GetGUID();
655 achievementEarned.Earner = _owner->GetGUID();
656 achievementEarned.EarnerNativeRealm = achievementEarned.EarnerVirtualRealm = GetVirtualRealmAddress();
657 achievementEarned.AchievementID = achievement->ID;
658 achievementEarned.Time = *GameTime::GetUtcWowTime();
659 achievementEarned.Time += receiver->GetSession()->GetTimezoneOffset();
660 receiver->SendDirectMessage(achievementEarned.Write());
661 };
662
663 if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG))
664 {
665 float dist = sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY);
666 Trinity::MessageDistDeliverer notifier(_owner, achievementEarnedBuilder, dist);
667 Cell::VisitWorldObjects(_owner, notifier, dist);
668 }
669 else
670 achievementEarnedBuilder(_owner);
671}
672
674{
676}
677
679{
680 return sCriteriaMgr->GetPlayerCriteriaByType(type, asset);
681}
682
684{
685}
686
688{
690
691 ObjectGuid guid = _owner->GetGUID();
692 for (std::pair<uint32 const, CompletedAchievementData> const& completedAchievement : _completedAchievements)
693 {
694 auto packetBuilder = [&](Player const* receiver)
695 {
697 guildAchievementDeleted.AchievementID = completedAchievement.first;
698 guildAchievementDeleted.GuildGUID = guid;
699 guildAchievementDeleted.TimeDeleted = *GameTime::GetUtcWowTime();
700 guildAchievementDeleted.TimeDeleted += receiver->GetSession()->GetTimezoneOffset();
701 receiver->SendDirectMessage(guildAchievementDeleted.Write());
702 };
703 _owner->BroadcastWorker(packetBuilder);
704 }
705
708 DeleteFromDB(guid);
709}
710
712{
713 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
714
716 stmt->setUInt64(0, guid.GetCounter());
717 trans->Append(stmt);
718
720 stmt->setUInt64(0, guid.GetCounter());
721 trans->Append(stmt);
722
723 CharacterDatabase.CommitTransaction(trans);
724}
725
727{
728 if (achievementResult)
729 {
730 do
731 {
732 Field* fields = achievementResult->Fetch();
733 uint32 achievementid = fields[0].GetUInt32();
734
735 // must not happen: cleanup at server startup in sAchievementMgr->LoadCompletedAchievements()
736 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementid);
737 if (!achievement)
738 continue;
739
741 ca.Date = fields[1].GetInt64();
742 for (std::string_view guid : Trinity::Tokenize(fields[2].GetStringView(), ',', false))
743 if (Optional<ObjectGuid::LowType> parsedGuid = Trinity::StringTo<ObjectGuid::LowType>(guid))
744 ca.CompletingPlayers.insert(ObjectGuid::Create<HighGuid::Player>(*parsedGuid));
745
746 ca.Changed = false;
747
748 _achievementPoints += achievement->Points;
749 } while (achievementResult->NextRow());
750 }
751
752 if (criteriaResult)
753 {
754 time_t now = GameTime::GetGameTime();
755 do
756 {
757 Field* fields = criteriaResult->Fetch();
758 uint32 id = fields[0].GetUInt32();
759 uint64 counter = fields[1].GetUInt64();
760 time_t date = fields[2].GetInt64();
761 ObjectGuid::LowType guid = fields[3].GetUInt64();
762
763 Criteria const* criteria = sCriteriaMgr->GetCriteria(id);
764 if (!criteria)
765 {
766 // we will remove not existed criteria for all guilds
767 TC_LOG_ERROR("criteria.achievement", "Non-existing achievement criteria {} data removed from table `guild_achievement_progress`.", id);
768
770 stmt->setUInt32(0, id);
771 CharacterDatabase.Execute(stmt);
772 continue;
773 }
774
775 if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now)
776 continue;
777
778 CriteriaProgress& progress = _criteriaProgress[id];
779 progress.Counter = counter;
780 progress.Date = date;
781 progress.PlayerGUID = ObjectGuid::Create<HighGuid::Player>(guid);
782 progress.Changed = false;
783 } while (criteriaResult->NextRow());
784 }
785}
786
788{
790 std::ostringstream guidstr;
791 for (std::pair<uint32 const, CompletedAchievementData>& completedAchievement : _completedAchievements)
792 {
793 if (!completedAchievement.second.Changed)
794 continue;
795
796 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT);
797 stmt->setUInt64(0, _owner->GetId());
798 stmt->setUInt32(1, completedAchievement.first);
799 trans->Append(stmt);
800
801 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT);
802 stmt->setUInt64(0, _owner->GetId());
803 stmt->setUInt32(1, completedAchievement.first);
804 stmt->setInt64(2, completedAchievement.second.Date);
805 for (ObjectGuid memberGuid : completedAchievement.second.CompletingPlayers)
806 guidstr << memberGuid.GetCounter() << ',';
807
808 stmt->setString(3, guidstr.str());
809 trans->Append(stmt);
810
811 guidstr.str("");
812
813 completedAchievement.second.Changed = false;
814 }
815
816 for (std::pair<uint32 const, CriteriaProgress>& criteriaProgres : _criteriaProgress)
817 {
818 if (!criteriaProgres.second.Changed)
819 continue;
820
821 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT_CRITERIA);
822 stmt->setUInt64(0, _owner->GetId());
823 stmt->setUInt32(1, criteriaProgres.first);
824 trans->Append(stmt);
825
826 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT_CRITERIA);
827 stmt->setUInt64(0, _owner->GetId());
828 stmt->setUInt32(1, criteriaProgres.first);
829 stmt->setUInt64(2, criteriaProgres.second.Counter);
830 stmt->setInt64(3, criteriaProgres.second.Date);
831 stmt->setUInt64(4, criteriaProgres.second.PlayerGUID.GetCounter());
832 trans->Append(stmt);
833
834 criteriaProgres.second.Changed = false;
835 }
836}
837
838void GuildAchievementMgr::SendAllData(Player const* receiver) const
839{
840 VisibleAchievementCheck filterInvisible;
842 allGuildAchievements.Earned.reserve(_completedAchievements.size());
843
844 for (std::pair<uint32 const, CompletedAchievementData> const& completedAchievement : _completedAchievements)
845 {
846 AchievementEntry const* achievement = filterInvisible(completedAchievement);
847 if (!achievement)
848 continue;
849
850 WorldPackets::Achievement::EarnedAchievement& earned = allGuildAchievements.Earned.emplace_back();
851 earned.Id = completedAchievement.first;
852 earned.Date.SetUtcTimeFromUnixTime(completedAchievement.second.Date);
853 earned.Date += receiver->GetSession()->GetTimezoneOffset();
854 }
855
856 receiver->SendDirectMessage(allGuildAchievements.Write());
857}
858
859void GuildAchievementMgr::SendAchievementInfo(Player* receiver, uint32 achievementId /*= 0*/) const
860{
862 if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId))
863 {
864 if (CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(achievement->CriteriaTree))
865 {
866 CriteriaMgr::WalkCriteriaTree(tree, [this, &guildCriteriaUpdate, receiver](CriteriaTree const* node)
867 {
868 if (node->Criteria)
869 {
870 auto progress = this->_criteriaProgress.find(node->Criteria->ID);
871 if (progress != this->_criteriaProgress.end())
872 {
873 WorldPackets::Achievement::GuildCriteriaProgress& guildCriteriaProgress = guildCriteriaUpdate.Progress.emplace_back();
874 guildCriteriaProgress.CriteriaID = node->Criteria->ID;
875 guildCriteriaProgress.DateCreated = 0;
876 guildCriteriaProgress.DateStarted = 0;
877 guildCriteriaProgress.DateUpdated.SetUtcTimeFromUnixTime(progress->second.Date);
878 guildCriteriaProgress.DateUpdated += receiver->GetSession()->GetTimezoneOffset();
879 guildCriteriaProgress.Quantity = progress->second.Counter;
880 guildCriteriaProgress.PlayerGUID = progress->second.PlayerGUID;
881 guildCriteriaProgress.Flags = 0;
882 }
883 }
884 });
885 }
886 }
887
888 receiver->SendDirectMessage(guildCriteriaUpdate.Write());
889}
890
891void GuildAchievementMgr::SendAllTrackedCriterias(Player* receiver, std::set<uint32> const& trackedCriterias) const
892{
894 guildCriteriaUpdate.Progress.reserve(trackedCriterias.size());
895
896 for (uint32 criteriaId : trackedCriterias)
897 {
898 auto progress = _criteriaProgress.find(criteriaId);
899 if (progress == _criteriaProgress.end())
900 continue;
901
902 WorldPackets::Achievement::GuildCriteriaProgress& guildCriteriaProgress = guildCriteriaUpdate.Progress.emplace_back();
903 guildCriteriaProgress.CriteriaID = criteriaId;
904 guildCriteriaProgress.DateCreated = 0;
905 guildCriteriaProgress.DateStarted = 0;
906 guildCriteriaProgress.DateUpdated.SetUtcTimeFromUnixTime(progress->second.Date);
907 guildCriteriaProgress.DateUpdated += receiver->GetSession()->GetTimezoneOffset();
908 guildCriteriaProgress.Quantity = progress->second.Counter;
909 guildCriteriaProgress.PlayerGUID = progress->second.PlayerGUID;
910 guildCriteriaProgress.Flags = 0;
911 }
912
913 receiver->SendDirectMessage(guildCriteriaUpdate.Write());
914}
915
917{
918 auto itr = _completedAchievements.find(achievementId);
919 if (itr != _completedAchievements.end())
920 {
922 guildAchievementMembers.GuildGUID = _owner->GetGUID();
923 guildAchievementMembers.AchievementID = achievementId;
924 guildAchievementMembers.Member.reserve(itr->second.CompletingPlayers.size());
925 for (ObjectGuid const& member : itr->second.CompletingPlayers)
926 guildAchievementMembers.Member.emplace_back(member);
927
928 receiver->SendDirectMessage(guildAchievementMembers.Write());
929 }
930}
931
932void GuildAchievementMgr::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer)
933{
934 TC_LOG_DEBUG("criteria.achievement", "GuildAchievementMgr::CompletedAchievement({})", achievement->ID);
935
936 if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID))
937 return;
938
940 if (Guild* guild = referencePlayer->GetGuild())
941 guild->AddGuildNews(GUILD_NEWS_GUILD_ACHIEVEMENT, ObjectGuid::Empty, achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID);
942
943 SendAchievementEarned(achievement);
946 ca.Changed = true;
947
949 {
950 if (referencePlayer->GetGuildId() == _owner->GetId())
951 ca.CompletingPlayers.insert(referencePlayer->GetGUID());
952
953 if (Group const* group = referencePlayer->GetGroup())
954 for (GroupReference const* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
955 if (Player const* groupMember = ref->GetSource())
956 if (groupMember->GetGuildId() == _owner->GetId())
957 ca.CompletingPlayers.insert(groupMember->GetGUID());
958 }
959
961 sAchievementMgr->SetRealmCompleted(achievement);
962
963 if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG))
964 _achievementPoints += achievement->Points;
965
966 UpdateCriteria(CriteriaType::EarnAchievement, achievement->ID, 0, 0, nullptr, referencePlayer);
967 UpdateCriteria(CriteriaType::EarnAchievementPoints, achievement->Points, 0, 0, nullptr, referencePlayer);
968
969 sScriptMgr->OnAchievementCompleted(referencePlayer, achievement);
970}
971
972void GuildAchievementMgr::SendCriteriaUpdate(Criteria const* entry, CriteriaProgress const* progress, Seconds /*timeElapsed*/, bool /*timedCompleted*/) const
973{
974 for (Player const* member : _owner->GetMembersTrackingCriteria(entry->ID))
975 {
977 WorldPackets::Achievement::GuildCriteriaProgress& guildCriteriaProgress = guildCriteriaUpdate.Progress.emplace_back();
978 guildCriteriaProgress.CriteriaID = entry->ID;
979 guildCriteriaProgress.DateCreated = 0;
980 guildCriteriaProgress.DateStarted = 0;
981 guildCriteriaProgress.DateUpdated.SetUtcTimeFromUnixTime(progress->Date);
982 guildCriteriaProgress.DateUpdated += member->GetSession()->GetTimezoneOffset();
983 guildCriteriaProgress.Quantity = progress->Counter;
984 guildCriteriaProgress.PlayerGUID = progress->PlayerGUID;
985 guildCriteriaProgress.Flags = 0;
986
987 member->SendDirectMessage(guildCriteriaUpdate.Write());
988 }
989}
990
992{
994 guildCriteriaDeleted.GuildGUID = _owner->GetGUID();
995 guildCriteriaDeleted.CriteriaID = criteriaId;
996 SendPacket(guildCriteriaDeleted.Write());
997}
998
1000{
1002 {
1003 // broadcast realm first reached
1005 serverFirstAchievement.Name = _owner->GetName();
1006 serverFirstAchievement.PlayerGUID = _owner->GetGUID();
1007 serverFirstAchievement.AchievementID = achievement->ID;
1008 serverFirstAchievement.GuildAchievement = true;
1009 sWorld->SendGlobalMessage(serverFirstAchievement.Write());
1010 }
1011
1012 auto guildAchievementEarnedBuilder = [&](Player const* receiver)
1013 {
1015 guildAchievementEarned.AchievementID = achievement->ID;
1016 guildAchievementEarned.GuildGUID = _owner->GetGUID();
1017 guildAchievementEarned.TimeEarned = *GameTime::GetUtcWowTime();
1018 guildAchievementEarned.TimeEarned += receiver->GetSession()->GetTimezoneOffset();
1019 receiver->SendDirectMessage(guildAchievementEarned.Write());
1020 };
1021 _owner->BroadcastWorker(guildAchievementEarnedBuilder);
1022}
1023
1025{
1026 _owner->BroadcastPacket(data);
1027}
1028
1030{
1031 return sCriteriaMgr->GetGuildCriteriaByType(type);
1032}
1033
1036
1038{
1039 return Trinity::StringFormat("{} {}", _owner->GetGUID().ToString(), _owner->GetName());
1040}
1041
1043{
1044 return Trinity::StringFormat("Guild ID {} {}", _owner->GetId(), _owner->GetName());
1045}
1046
1048{
1049 static AchievementGlobalMgr instance;
1050 return &instance;
1051}
1052
1053std::vector<AchievementEntry const*> const* AchievementGlobalMgr::GetAchievementByReferencedId(uint32 id) const
1054{
1055 auto itr = _achievementListByReferencedId.find(id);
1056 return itr != _achievementListByReferencedId.end() ? &itr->second : nullptr;
1057}
1058
1060{
1061 auto iter = _achievementRewards.find(achievement->ID);
1062 return iter != _achievementRewards.end() ? &iter->second : nullptr;
1063}
1064
1066{
1067 auto iter = _achievementRewardLocales.find(achievement->ID);
1068 return iter != _achievementRewardLocales.end() ? &iter->second : nullptr;
1069}
1070
1072{
1073 auto itr = _allCompletedAchievements.find(achievement->ID);
1074 if (itr == _allCompletedAchievements.end())
1075 return false;
1076
1077 if (itr->second == SystemTimePoint ::min())
1078 return false;
1079
1080 if (itr->second == SystemTimePoint::max())
1081 return true;
1082
1083 // Allow completing the realm first kill for entire minute after first person did it
1084 // it may allow more than one group to achieve it (highly unlikely)
1085 // but apparently this is how blizz handles it as well
1086 if (achievement->Flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)
1087 return (GameTime::GetSystemTime() - itr->second) > Minutes(1);
1088
1089 return true;
1090}
1091
1093{
1094 if (IsRealmCompleted(achievement))
1095 return;
1096
1098}
1099
1100//==========================================================
1102{
1103 uint32 oldMSTime = getMSTime();
1104
1105 if (sAchievementStore.GetNumRows() == 0)
1106 {
1107 TC_LOG_INFO("server.loading", ">> Loaded 0 achievement references.");
1108 return;
1109 }
1110
1111 uint32 count = 0;
1112
1113 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1114 {
1115 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1116 if (!achievement || !achievement->SharesCriteria)
1117 continue;
1118
1119 _achievementListByReferencedId[achievement->SharesCriteria].push_back(achievement);
1120 ++count;
1121 }
1122
1124
1125 // Once Bitten, Twice Shy (10 player) - Icecrown Citadel
1126 // Correct map requirement (currently has Ulduar); 6.0.3 note - it STILL has ulduar requirement
1127 hotfixes.ApplyHotfix(4539, [](AchievementEntry* achievement)
1128 {
1129 achievement->InstanceID = 631;
1130 });
1131
1132 TC_LOG_INFO("server.loading", ">> Loaded {} achievement references in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
1133}
1134
1136{
1137 uint32 oldMSTime = getMSTime();
1138
1139 _achievementScripts.clear(); // need for reload case
1140
1141 QueryResult result = WorldDatabase.Query("SELECT AchievementId, ScriptName FROM achievement_scripts");
1142 if (!result)
1143 {
1144 TC_LOG_INFO("server.loading", ">> Loaded 0 achievement scripts. DB table `achievement_scripts` is empty.");
1145 return;
1146 }
1147
1148 do
1149 {
1150 Field* fields = result->Fetch();
1151
1152 uint32 achievementId = fields[0].GetUInt32();
1153 std::string scriptName = fields[1].GetString();
1154
1155 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId);
1156 if (!achievement)
1157 {
1158 TC_LOG_ERROR("sql.sql", "Table `achievement_scripts` contains non-existing Achievement (ID: {}), skipped.", achievementId);
1159 continue;
1160 }
1161 _achievementScripts[achievementId] = sObjectMgr->GetScriptId(scriptName);
1162 }
1163 while (result->NextRow());
1164
1165 TC_LOG_INFO("server.loading", ">> Loaded {} achievement scripts in {} ms", _achievementScripts.size(), GetMSTimeDiffToNow(oldMSTime));
1166}
1167
1169{
1170 uint32 oldMSTime = getMSTime();
1171
1172 // Populate _allCompletedAchievements with all realm first achievement ids to make multithreaded access safer
1173 // while it will not prevent races, it will prevent crashes that happen because std::unordered_map key was added
1174 // instead the only potential race will happen on value associated with the key
1175 for (AchievementEntry const* achievement : sAchievementStore)
1177 _allCompletedAchievements[achievement->ID] = SystemTimePoint::min();
1178
1179 QueryResult result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1180
1181 if (!result)
1182 {
1183 TC_LOG_INFO("server.loading", ">> Loaded 0 realm first completed achievements. DB table `character_achievement` is empty.");
1184 return;
1185 }
1186
1187 do
1188 {
1189 Field* fields = result->Fetch();
1190
1191 uint32 achievementId = fields[0].GetUInt32();
1192 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId);
1193 if (!achievement)
1194 {
1195 // Remove non-existing achievements from all characters
1196 TC_LOG_ERROR("criteria.achievement", "Non-existing achievement {} data has been removed from the table `character_achievement`.", achievementId);
1197
1199 stmt->setUInt32(0, achievementId);
1200 CharacterDatabase.Execute(stmt);
1201
1202 continue;
1203 }
1205 _allCompletedAchievements[achievementId] = SystemTimePoint::max();
1206 }
1207 while (result->NextRow());
1208
1209 TC_LOG_INFO("server.loading", ">> Loaded {} realm first completed achievements in {} ms.", (unsigned long)_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime));
1210}
1211
1213{
1214 uint32 oldMSTime = getMSTime();
1215
1216 _achievementRewards.clear(); // need for reload case
1217
1218 // 0 1 2 3 4 5 6 7
1219 QueryResult result = WorldDatabase.Query("SELECT ID, TitleA, TitleH, ItemID, Sender, Subject, Body, MailTemplateID FROM achievement_reward");
1220
1221 if (!result)
1222 {
1223 TC_LOG_INFO("server.loading", ">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1224 return;
1225 }
1226
1227 do
1228 {
1229 Field* fields = result->Fetch();
1230 uint32 id = fields[0].GetUInt32();
1231 AchievementEntry const* achievement = sAchievementStore.LookupEntry(id);
1232 if (!achievement)
1233 {
1234 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement ID ({}), ignored.", id);
1235 continue;
1236 }
1237
1238 AchievementReward reward;
1239 reward.TitleId[0] = fields[1].GetUInt32();
1240 reward.TitleId[1] = fields[2].GetUInt32();
1241 reward.ItemId = fields[3].GetUInt32();
1242 reward.SenderCreatureId = fields[4].GetUInt32();
1243 reward.Subject = fields[5].GetString();
1244 reward.Body = fields[6].GetString();
1245 reward.MailTemplateId = fields[7].GetUInt32();
1246
1247 // must be title or mail at least
1248 if (!reward.TitleId[0] && !reward.TitleId[1] && !reward.SenderCreatureId)
1249 {
1250 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) does not contain title or item reward data. Ignored.", id);
1251 continue;
1252 }
1253
1254 if (achievement->Faction == ACHIEVEMENT_FACTION_ANY && (!reward.TitleId[0] ^ !reward.TitleId[1]))
1255 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) contains the title (A: {} H: {}) for only one team.", id, reward.TitleId[0], reward.TitleId[1]);
1256
1257 if (reward.TitleId[0])
1258 {
1259 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[0]);
1260 if (!titleEntry)
1261 {
1262 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: {}) contains an invalid title id ({}) in `title_A`, set to 0", id, reward.TitleId[0]);
1263 reward.TitleId[0] = 0;
1264 }
1265 }
1266
1267 if (reward.TitleId[1])
1268 {
1269 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[1]);
1270 if (!titleEntry)
1271 {
1272 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: {}) contains an invalid title id ({}) in `title_H`, set to 0", id, reward.TitleId[1]);
1273 reward.TitleId[1] = 0;
1274 }
1275 }
1276
1277 //check mail data before item for report including wrong item case
1278 if (reward.SenderCreatureId)
1279 {
1280 if (!sObjectMgr->GetCreatureTemplate(reward.SenderCreatureId))
1281 {
1282 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) contains an invalid creature ID {} as sender, mail reward skipped.", id, reward.SenderCreatureId);
1283 reward.SenderCreatureId = 0;
1284 }
1285 }
1286 else
1287 {
1288 if (reward.ItemId)
1289 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) does not have sender data, but contains an item reward. Item will not be rewarded.", id);
1290
1291 if (!reward.Subject.empty())
1292 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) does not have sender data, but contains a mail subject.", id);
1293
1294 if (!reward.Body.empty())
1295 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) does not have sender data, but contains mail text.", id);
1296
1297 if (reward.MailTemplateId)
1298 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) does not have sender data, but has a MailTemplate.", id);
1299 }
1300
1301 if (reward.MailTemplateId)
1302 {
1303 if (!sMailTemplateStore.LookupEntry(reward.MailTemplateId))
1304 {
1305 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) is using an invalid MailTemplate ({}).", id, reward.MailTemplateId);
1306 reward.MailTemplateId = 0;
1307 }
1308 else if (!reward.Subject.empty() || !reward.Body.empty())
1309 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) is using MailTemplate ({}) and mail subject/text.", id, reward.MailTemplateId);
1310 }
1311
1312 if (reward.ItemId)
1313 {
1314 if (!sObjectMgr->GetItemTemplate(reward.ItemId))
1315 {
1316 TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: {}) contains an invalid item id {}, reward mail will not contain the rewarded item.", id, reward.ItemId);
1317 reward.ItemId = 0;
1318 }
1319 }
1320
1321 _achievementRewards[id] = reward;
1322 } while (result->NextRow());
1323
1324 TC_LOG_INFO("server.loading", ">> Loaded {} achievement rewards in {} ms.", uint32(_achievementRewards.size()), GetMSTimeDiffToNow(oldMSTime));
1325}
1326
1328{
1329 uint32 oldMSTime = getMSTime();
1330
1331 _achievementRewardLocales.clear(); // need for reload case
1332
1333 // 0 1 2 3
1334 QueryResult result = WorldDatabase.Query("SELECT ID, Locale, Subject, Body FROM achievement_reward_locale");
1335
1336 if (!result)
1337 {
1338 TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `achievement_reward_locale` is empty.");
1339 return;
1340 }
1341
1342 do
1343 {
1344 Field* fields = result->Fetch();
1345
1346 uint32 id = fields[0].GetUInt32();
1347 std::string_view localeName = fields[1].GetStringView();
1348
1349 if (_achievementRewards.find(id) == _achievementRewards.end())
1350 {
1351 TC_LOG_ERROR("sql.sql", "Table `achievement_reward_locale` (ID: {}) contains locale strings for a non-existing achievement reward.", id);
1352 continue;
1353 }
1354
1355 LocaleConstant locale = GetLocaleByName(localeName);
1356 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
1357 continue;
1358
1360 ObjectMgr::AddLocaleString(fields[2].GetStringView(), locale, data.Subject);
1361 ObjectMgr::AddLocaleString(fields[3].GetStringView(), locale, data.Body);
1362 } while (result->NextRow());
1363
1364 TC_LOG_INFO("server.loading", ">> Loaded {} achievement reward locale strings in {} ms.", uint32(_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime));
1365}
1366
1368{
1369 if (uint32 const* scriptId = Trinity::Containers::MapGetValuePtr(_achievementScripts, achievementId))
1370 return *scriptId;
1371 return 0;
1372}
#define sAchievementMgr
@ CHAR_INS_GUILD_ACHIEVEMENT_CRITERIA
@ CHAR_DEL_ALL_GUILD_ACHIEVEMENTS
@ CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA
@ CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS
@ CHAR_INS_CHAR_ACHIEVEMENT
@ CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT
@ CHAR_DEL_INVALID_ACHIEVMENT
@ CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS
@ CHAR_INS_GUILD_ACHIEVEMENT
@ CHAR_DEL_GUILD_ACHIEVEMENT
@ CHAR_DEL_GUILD_ACHIEVEMENT_CRITERIA
@ CHAR_DEL_ALL_GUILD_ACHIEVEMENT_CRITERIA
@ CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA
@ CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA_GUILD
@ CHAR_DEL_CHAR_ACHIEVEMENT
LocaleConstant GetLocaleByName(std::string_view name)
Definition: Common.cpp:36
LocaleConstant
Definition: Common.h:48
@ LOCALE_enUS
Definition: Common.h:49
constexpr bool IsValidLocale(LocaleConstant locale)
Definition: Common.h:95
@ CRITERIA_FLAG_CU_ACCOUNT
@ CRITERIA_FLAG_CU_PLAYER
#define sCriteriaMgr
std::vector< Criteria const * > CriteriaList
DB2Storage< AchievementEntry > sAchievementStore("Achievement.db2", &AchievementLoadInfo::Instance)
DB2Storage< CharTitlesEntry > sCharTitlesStore("CharTitles.db2", &CharTitlesLoadInfo::Instance)
DB2Storage< MailTemplateEntry > sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance)
@ ACHIEVEMENT_FACTION_HORDE
Definition: DBCEnums.h:77
@ ACHIEVEMENT_FACTION_ALLIANCE
Definition: DBCEnums.h:78
@ ACHIEVEMENT_FACTION_ANY
Definition: DBCEnums.h:79
@ ACHIEVEMENT_FLAG_TRACKING_FLAG
Definition: DBCEnums.h:104
@ ACHIEVEMENT_FLAG_COUNTER
Definition: DBCEnums.h:84
@ ACHIEVEMENT_FLAG_ACCOUNT
Definition: DBCEnums.h:101
@ ACHIEVEMENT_FLAG_SHOW_GUILD_MEMBERS
Definition: DBCEnums.h:99
@ ACHIEVEMENT_FLAG_HIDDEN
Definition: DBCEnums.h:85
@ ACHIEVEMENT_FLAG_REALM_FIRST_REACH
Definition: DBCEnums.h:92
@ ACHIEVEMENT_FLAG_REALM_FIRST_KILL
Definition: DBCEnums.h:93
@ ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER
Definition: DBCEnums.h:97
@ ACHIEVEMENT_FLAG_SHOW_IN_GUILD_NEWS
Definition: DBCEnums.h:96
@ ACHIEVEMENT_FLAG_SUMM
Definition: DBCEnums.h:87
CriteriaType
Definition: DBCEnums.h:503
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
int64_t int64
Definition: Define.h:137
uint64_t uint64
Definition: Define.h:141
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition: Duration.h:32
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition: Duration.h:35
#define sGuildMgr
Definition: GuildMgr.h:70
@ GUILD_NEWS_GUILD_ACHIEVEMENT
Definition: Guild.h:240
@ GUILD_NEWS_PLAYER_ACHIEVEMENT
Definition: Guild.h:241
@ BROADCAST_TEXT_ACHIEVEMENT_EARNED
Definition: Language.h:23
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_TRACE(filterType__,...)
Definition: Log.h:153
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
@ MAIL_CREATURE
Definition: Mail.h:41
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
Role Based Access Control related classes definition.
if(posix_memalign(&__mallocedMemory, __align, __size)) return NULL
#define sScriptMgr
Definition: ScriptMgr.h:1418
@ ALLIANCE
@ HORDE
@ CHAT_MSG_GUILD_ACHIEVEMENT
@ CHAT_MSG_ACHIEVEMENT
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
AchievementRewardLocale const * GetAchievementRewardLocale(AchievementEntry const *achievement) const
std::unordered_map< uint32, AchievementRewardLocale > _achievementRewardLocales
std::unordered_map< uint32, uint32 > _achievementScripts
bool IsRealmCompleted(AchievementEntry const *achievement) const
std::unordered_map< uint32, std::vector< AchievementEntry const * > > _achievementListByReferencedId
void SetRealmCompleted(AchievementEntry const *achievement)
std::unordered_map< uint32, AchievementReward > _achievementRewards
static AchievementGlobalMgr * Instance()
AchievementReward const * GetAchievementReward(AchievementEntry const *achievement) const
uint32 GetAchievementScriptId(uint32 achievementId) const
std::vector< AchievementEntry const * > const * GetAchievementByReferencedId(uint32 id) const
std::unordered_map< uint32, SystemTimePoint > _allCompletedAchievements
bool HasAchieved(uint32 achievementId) const
uint32 _achievementPoints
bool RequiredAchievementSatisfied(uint32 achievementId) const override
virtual void CompletedAchievement(AchievementEntry const *entry, Player *referencePlayer)=0
void CompletedCriteriaTree(CriteriaTree const *tree, Player *referencePlayer) override
void CheckAllAchievementCriteria(Player *referencePlayer)
bool IsCompletedAchievement(AchievementEntry const *entry)
std::unordered_map< uint32, CompletedAchievementData > _completedAchievements
uint32 GetAchievementPoints() const
void AfterCriteriaTreeUpdate(CriteriaTree const *tree, Player *referencePlayer) override
bool CanCompleteCriteriaTree(CriteriaTree const *tree) override
bool CanUpdateCriteriaTree(Criteria const *criteria, CriteriaTree const *tree, Player *referencePlayer) const override
std::vector< uint32 > GetCompletedAchievementIds() const
virtual void Reset()
CriteriaProgress * GetCriteriaProgress(Criteria const *entry)
virtual bool CanUpdateCriteriaTree(Criteria const *criteria, CriteriaTree const *tree, Player *referencePlayer) const
CriteriaProgressMap _criteriaProgress
bool IsCompletedCriteriaTree(CriteriaTree const *tree)
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject const *ref=nullptr, Player *referencePlayer=nullptr)
static char const * GetCriteriaTypeString(CriteriaType type)
static void WalkCriteriaTree(CriteriaTree const *tree, Func const &func)
void ApplyHotfix(uint32 id, void(*fixer)(T *), bool notifyClient=false)
Class used to access individual fields of database query result.
Definition: Field.h:90
std::string GetString() const
Definition: Field.cpp:118
int64 GetInt64() const
Definition: Field.cpp:86
std::string_view GetStringView() const
Definition: Field.cpp:130
uint64 GetUInt64() const
Definition: Field.cpp:78
uint32 GetUInt32() const
Definition: Field.cpp:62
GroupReference * next()
Definition: Group.h:197
void Reset() override
std::string GetOwnerInfo() const override
void SendCriteriaUpdate(Criteria const *entry, CriteriaProgress const *progress, Seconds timeElapsed, bool timedCompleted) const override
void SendAchievementEarned(AchievementEntry const *achievement) const
void SendAchievementInfo(Player *receiver, uint32 achievementId=0) const
void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult)
GuildAchievementMgr(Guild *owner)
static void DeleteFromDB(ObjectGuid const &guid)
void CompletedAchievement(AchievementEntry const *entry, Player *referencePlayer) override
void SendCriteriaProgressRemoved(uint32 criteriaId) override
CriteriaList const & GetCriteriaByType(CriteriaType type, uint32 asset) const override
void SendAchievementMembers(Player *receiver, uint32 achievementId) const
void SendAllData(Player const *receiver) const override
void SendPacket(WorldPacket const *data) const override
void SendAllTrackedCriterias(Player *receiver, std::set< uint32 > const &trackedCriterias) const
void SaveToDB(CharacterDatabaseTransaction trans)
Definition: Guild.h:329
void BroadcastPacket(WorldPacket const *packet) const
Definition: Guild.cpp:2763
std::vector< Player * > GetMembersTrackingCriteria(uint32 criteriaId) const
Definition: Guild.cpp:2770
ObjectGuid::LowType GetId() const
Definition: Guild.h:752
std::string const & GetName() const
Definition: Guild.h:755
void BroadcastWorker(Do &&_do, Player const *except=nullptr) const
Definition: Guild.h:836
ObjectGuid GetGUID() const
Definition: Guild.h:753
Definition: Item.h:170
virtual void SaveToDB(CharacterDatabaseTransaction trans)
Definition: Item.cpp:561
static Item * CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const *player=nullptr, bool addDefaultBonuses=true)
Definition: Item.cpp:1625
void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const &receiver, MailSender const &sender, MailCheckMask checked=MAIL_CHECK_MASK_NONE, uint32 deliver_delay=0)
Definition: Mail.cpp:192
MailDraft & AddItem(Item *item)
Definition: Mail.cpp:99
LowType GetCounter() const
Definition: ObjectGuid.h:293
static ObjectGuid const Empty
Definition: ObjectGuid.h:274
std::string ToString() const
Definition: ObjectGuid.cpp:554
uint64 LowType
Definition: ObjectGuid.h:278
static void AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector< std::string > &data)
Definition: ObjectMgr.cpp:240
static std::string_view GetLocaleString(std::vector< std::string > const &data, LocaleConstant locale)
Definition: ObjectMgr.h:1682
bool IsInWorld() const
Definition: Object.h:154
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
bool ModifierTreeSatisfied(uint32 modifierTreeId) const
void SendAllData(Player const *receiver) const override
void SendAchievementEarned(AchievementEntry const *achievement) const
void CompletedAchievement(AchievementEntry const *entry, Player *referencePlayer) override
static void DeleteFromDB(ObjectGuid const &guid)
void SendCriteriaUpdate(Criteria const *entry, CriteriaProgress const *progress, Seconds timeElapsed, bool timedCompleted) const override
void SendAchievementInfo(Player *receiver, uint32 achievementId=0) const
void Reset() override
void SaveToDB(CharacterDatabaseTransaction trans)
void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult)
void SendCriteriaProgressRemoved(uint32 criteriaId) override
PlayerAchievementMgr(Player *owner)
void SendPacket(WorldPacket const *data) const override
CriteriaList const & GetCriteriaByType(CriteriaType type, uint32 asset) const override
std::string GetOwnerInfo() const override
static Team TeamForRace(uint8 race)
Definition: Player.cpp:6454
UF::UpdateField< UF::PlayerData, 0, TYPEID_PLAYER > m_playerData
Definition: Player.h:2863
Gender GetNativeGender() const override
Definition: Player.h:1217
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6324
Guild * GetGuild()
Definition: Player.cpp:29432
WorldSession * GetSession() const
Definition: Player.h:2101
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition: Player.cpp:26767
ObjectGuid::LowType GetGuildId() const
Definition: Player.h:1993
bool IsGameMaster() const
Definition: Player.h:1178
void SetTitle(CharTitlesEntry const *title, bool lost=false)
Definition: Player.cpp:26283
Group * GetGroup(Optional< uint8 > partyIndex)
Definition: Player.h:2606
Team GetTeam() const
Definition: Player.h:2235
void setInt64(const uint8 index, const int64 value)
void setUInt32(const uint8 index, const uint32 value)
void setString(const uint8 index, const std::string &value)
void setUInt64(const uint8 index, const uint64 value)
uint8 GetRace() const
Definition: Unit.h:749
std::string const & GetName() const
Definition: Object.h:555
std::vector< CriteriaProgress > Progress
std::vector< GuildAchievementMember > Member
std::vector< GuildCriteriaProgress > Progress
ObjectGuid GetBattlenetAccountGUID() const
LocaleConstant GetSessionDbLocaleIndex() const
Minutes GetTimezoneOffset() const
bool PlayerLoading() const
Definition: WorldSession.h:969
bool HasPermission(uint32 permissionId)
void SetUtcTimeFromUnixTime(std::time_t unixTime)
Definition: WowTime.cpp:86
#define sWorld
Definition: World.h:931
uint32 GetVirtualRealmAddress()
Definition: World.cpp:3968
@ CONFIG_LISTEN_RANGE_SAY
Definition: World.h:209
WowTime const * GetUtcWowTime()
Definition: GameTime.cpp:92
SystemTimePoint GetSystemTime()
Current chrono system_clock time point.
Definition: GameTime.cpp:54
time_t GetGameTime()
Definition: GameTime.cpp:44
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
@ RBAC_PERM_CANNOT_EARN_REALM_FIRST_ACHIEVEMENTS
Definition: RBAC.h:62
@ RBAC_PERM_CANNOT_EARN_ACHIEVEMENTS
Definition: RBAC.h:61
std::vector< std::string > Body
std::vector< std::string > Subject
std::string Subject
std::string Body
static void VisitWorldObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition: CellImpl.h:191
std::time_t Date
ObjectGuid PlayerGUID
::Criteria const * Criteria
CriteriaTreeEntry const * Entry
AchievementEntry const * Achievement
CriteriaEntry const * Entry
uint32 FlagsCu
AchievementEntry const * operator()(std::pair< uint32 const, CompletedAchievementData > const &val)
std::vector< CriteriaProgress > Progress
std::vector< EarnedAchievement > Earned