Core/Dungeon Finder: Added support for rewards for random dungeons
Core/Quests: Simplified rewarding money from quests, now ModifyMoney and UpdateAchievementCriteria are called only once per quest Core/Achievements: Implemented ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS --HG-- branch : trunk
This commit is contained in:
@@ -592,6 +592,8 @@ INSERT INTO `command` VALUES
|
||||
('reload item_enchantment_template',3,'Syntax: .reload item_enchantment_template\nReload item_enchantment_template table.'),
|
||||
('reload item_loot_template',3,'Syntax: .reload item_loot_template\nReload item_loot_template table.'),
|
||||
('reload item_set_names',3,'Syntax: .reload item_set_names\nReload item_set_names table.'),
|
||||
('reload lfg_dungeon_encounters',3,'Syntax: .reload lfg_dungeon_encounters\nReload lfg_dungeon_encounters table.'),
|
||||
('reload lfg_dungeon_rewards',3,'Syntax: .reload lfg_dungeon_rewards\nReload lfg_dungeon_rewards table.'),
|
||||
('reload locales_creature',3,'Syntax: .reload locales_creature\nReload locales_creature table.'),
|
||||
('reload locales_gameobject',3,'Syntax: .reload locales_gameobject\nReload locales_gameobject table.'),
|
||||
('reload locales_gossip_menu_option',3, 'Syntax: .reload locales_gossip_menu_option\nReload locales_gossip_menu_option table.'),
|
||||
@@ -3090,6 +3092,52 @@ LOCK TABLES `item_template` WRITE;
|
||||
/*!40000 ALTER TABLE `item_template` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `lfg_dungeon_encounters`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `lfg_dungeon_encounters`;
|
||||
CREATE TABLE `lfg_dungeon_encounters` (
|
||||
`achievementId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Achievement marking final boss completion',
|
||||
`dungeonId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Dungeon entry from dbc',
|
||||
PRIMARY KEY (`achievementId`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
-- Dumping data for table `lfg_dungeon_encounters`
|
||||
--
|
||||
|
||||
LOCK TABLES `lfg_dungeon_encounters` WRITE;
|
||||
/*!40000 ALTER TABLE `lfg_dungeon_encounters` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `lfg_dungeon_encounters` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `lfg_dungeon_rewards`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `lfg_dungeon_rewards`;
|
||||
CREATE TABLE `lfg_dungeon_rewards` (
|
||||
`dungeonId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Dungeon entry from dbc',
|
||||
`maxLevel` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT 'Max level at which this reward is rewarded',
|
||||
`firstQuestId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Quest id with rewards for first dungeon this day',
|
||||
`firstMoneyVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Money multiplier for completing the dungeon first time in a day with less players than max',
|
||||
`firstXPVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Experience multiplier for completing the dungeon first time in a day with less players than max',
|
||||
`otherQuestId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Quest id with rewards for Nth dungeon this day',
|
||||
`otherMoneyVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Money multiplier for completing the dungeon Nth time in a day with less players than max',
|
||||
`otherXPVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Experience multiplier for completing the dungeon Nth time in a day with less players than max',
|
||||
PRIMARY KEY (`dungeonId`,`maxLevel`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
-- Dumping data for table `lfg_dungeon_rewards`
|
||||
--
|
||||
|
||||
LOCK TABLES `lfg_dungeon_rewards` WRITE;
|
||||
/*!40000 ALTER TABLE `lfg_dungeon_rewards` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `lfg_dungeon_rewards` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `locales_achievement_reward`
|
||||
--
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
DELETE FROM `command` WHERE `name` IN ('reload lfg_dungeon_encounters','reload lfg_dungeon_rewards');
|
||||
INSERT INTO `command` (`name`,`security`,`help`) VALUES
|
||||
('reload lfg_dungeon_encounters',3,'Syntax: .reload lfg_dungeon_encounters\nReload lfg_dungeon_encounters table.'),
|
||||
('reload lfg_dungeon_rewards',3,'Syntax: .reload lfg_dungeon_rewards\nReload lfg_dungeon_rewards table.');
|
||||
@@ -0,0 +1,6 @@
|
||||
DROP TABLE IF EXISTS `lfg_dungeon_encounters`;
|
||||
CREATE TABLE `lfg_dungeon_encounters` (
|
||||
`achievementId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Achievement marking final boss completion',
|
||||
`dungeonId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Dungeon entry from dbc',
|
||||
PRIMARY KEY (`achievementId`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -0,0 +1,12 @@
|
||||
DROP TABLE IF EXISTS `lfg_dungeon_rewards`;
|
||||
CREATE TABLE `lfg_dungeon_rewards` (
|
||||
`dungeonId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Dungeon entry from dbc',
|
||||
`maxLevel` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT 'Max level at which this reward is rewarded',
|
||||
`firstQuestId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Quest id with rewards for first dungeon this day',
|
||||
`firstMoneyVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Money multiplier for completing the dungeon first time in a day with less players than max',
|
||||
`firstXPVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Experience multiplier for completing the dungeon first time in a day with less players than max',
|
||||
`otherQuestId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Quest id with rewards for Nth dungeon this day',
|
||||
`otherMoneyVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Money multiplier for completing the dungeon Nth time in a day with less players than max',
|
||||
`otherXPVar` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Experience multiplier for completing the dungeon Nth time in a day with less players than max',
|
||||
PRIMARY KEY (`dungeonId`,`maxLevel`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "BattlegroundAB.h"
|
||||
#include "Map.h"
|
||||
#include "InstanceScript.h"
|
||||
#include "LFGMgr.h"
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
@@ -744,6 +745,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
|
||||
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
|
||||
if (!miscvalue1)
|
||||
continue;
|
||||
@@ -1476,9 +1478,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
|
||||
break; // Not implemented yet :(
|
||||
}
|
||||
|
||||
if (IsCompletedCriteria(achievementCriteria,achievement))
|
||||
CompletedCriteriaFor(achievement);
|
||||
|
||||
@@ -1496,6 +1498,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
|
||||
if (IsCompletedAchievement(*itr))
|
||||
CompletedAchievement(*itr);
|
||||
}
|
||||
|
||||
if (const uint32 dungeonId = sLFGMgr.GetDungeonIdForAchievement(achievement->ID))
|
||||
sLFGMgr.RewardDungeonDoneFor(dungeonId, GetPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1625,6 +1630,8 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
|
||||
return progress->counter >= achievementCriteria->honorable_kill.killCount;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS:
|
||||
return progress->counter >= 9000;
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
|
||||
return progress->counter >= achievementCriteria->use_lfg.dungeonsComplete;
|
||||
// handle all statistic-only criteria here
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
|
||||
|
||||
@@ -492,6 +492,8 @@ ChatCommand * ChatHandler::getCommandTable()
|
||||
{ "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
|
||||
{ "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
|
||||
{ "item_set_names", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemSetNamesCommand, "", NULL },
|
||||
{ "lfg_dungeon_encounters", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLfgEncountersCommand, "", NULL },
|
||||
{ "lfg_dungeon_rewards", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLfgRewardsCommand, "", NULL },
|
||||
{ "locales_achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesAchievementRewardCommand,"", NULL },
|
||||
{ "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL },
|
||||
{ "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL },
|
||||
|
||||
@@ -387,6 +387,8 @@ class ChatHandler
|
||||
bool HandleReloadGOQuestInvRelationsCommand(const char* args);
|
||||
bool HandleReloadItemEnchantementsCommand(const char* args);
|
||||
bool HandleReloadItemSetNamesCommand(const char* args);
|
||||
bool HandleReloadLfgEncountersCommand(const char* args);
|
||||
bool HandleReloadLfgRewardsCommand(const char* args);
|
||||
bool HandleReloadLocalesAchievementRewardCommand(const char* args);
|
||||
bool HandleReloadLocalesCreatureCommand(const char* args);
|
||||
bool HandleReloadLocalesGameobjectCommand(const char* args);
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "Transport.h"
|
||||
#include "WeatherMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "LFGMgr.h"
|
||||
|
||||
//reload commands
|
||||
bool ChatHandler::HandleReloadAllCommand(const char*)
|
||||
@@ -1055,6 +1056,22 @@ bool ChatHandler::HandleReloadLocalesAchievementRewardCommand(const char*)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatHandler::HandleReloadLfgEncountersCommand(const char*)
|
||||
{
|
||||
sLog.outString("Re-Loading dungeon encounter lfg associations...");
|
||||
sLFGMgr.LoadDungeonEncounters();
|
||||
SendGlobalGMSysMessage("DB table `lfg_dungeon_encounters` reloaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatHandler::HandleReloadLfgRewardsCommand(const char*)
|
||||
{
|
||||
sLog.outString("Re-Loading lfg dungeon rewards...");
|
||||
sLFGMgr.LoadRewards();
|
||||
SendGlobalGMSysMessage("DB table `lfg_dungeon_rewards` reloaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatHandler::HandleReloadLocalesCreatureCommand(const char* /*arg*/)
|
||||
{
|
||||
sLog.outString("Re-Loading Locales Creature ...");
|
||||
|
||||
@@ -481,6 +481,12 @@ struct AchievementCriteriaEntry
|
||||
uint32 killCount; // 4
|
||||
} honorable_kill;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 unused;
|
||||
uint32 dungeonsComplete;
|
||||
} use_lfg;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 field3; // 3 main requirement
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "ProgressBar.h"
|
||||
|
||||
LFGMgr::LFGMgr()
|
||||
{
|
||||
@@ -43,15 +44,11 @@ LFGMgr::LFGMgr()
|
||||
|
||||
LFGMgr::~LFGMgr()
|
||||
{
|
||||
// FIXME: RewardList to be removed -> query quest system
|
||||
for (LfgRewardList::iterator it = m_RewardList.begin(); it != m_RewardList.end(); ++it)
|
||||
delete *it;
|
||||
m_RewardList.clear();
|
||||
for (LfgRewardMap::iterator itr = m_RewardMap.begin(); itr != m_RewardMap.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_RewardMap.clear();
|
||||
|
||||
// FIXME: RewardDoneList to be removed -> query quest system
|
||||
for (LfgRewardList::iterator it = m_RewardDoneList.begin(); it != m_RewardDoneList.end(); ++it)
|
||||
delete *it;
|
||||
m_RewardDoneList.clear();
|
||||
m_EncountersByAchievement.clear();
|
||||
|
||||
for (LfgQueueInfoMap::iterator it = m_QueueInfoMap.begin(); it != m_QueueInfoMap.end(); ++it)
|
||||
delete it->second;
|
||||
@@ -363,39 +360,137 @@ void LFGMgr::Update(uint32 diff)
|
||||
/// </summary>
|
||||
void LFGMgr::InitLFG()
|
||||
{
|
||||
// Fill reward data (to be removed -> query quest system)
|
||||
LfgReward *reward;
|
||||
for (uint8 i = 0; i <= LFG_REWARD_DATA_SIZE; ++i)
|
||||
{
|
||||
reward = new LfgReward();
|
||||
reward->strangers = 0;
|
||||
reward->baseXP = RewardDungeonData[i][0];
|
||||
reward->baseMoney = RewardDungeonData[i][1];
|
||||
reward->variableMoney = 0;
|
||||
reward->variableXP = 0;
|
||||
reward->itemId = RewardDungeonData[i][2];
|
||||
reward->displayId = RewardDungeonData[i][3];
|
||||
reward->stackCount = RewardDungeonData[i][4];
|
||||
m_RewardList.push_back(reward);
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < LFG_REWARD_DATA_SIZE; ++i)
|
||||
{
|
||||
reward = new LfgReward();
|
||||
reward->strangers = 0;
|
||||
reward->baseXP = RewardDungeonDoneData[i][0];
|
||||
reward->baseMoney = RewardDungeonDoneData[i][1];
|
||||
reward->variableMoney = 0;
|
||||
reward->variableXP = 0;
|
||||
reward->itemId = RewardDungeonDoneData[i][2];
|
||||
reward->displayId = RewardDungeonDoneData[i][3];
|
||||
reward->stackCount = RewardDungeonDoneData[i][4];
|
||||
m_RewardDoneList.push_back(reward);
|
||||
}
|
||||
// Initialize dungeonMap
|
||||
GetAllDungeons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load achievement <-> encounter associations
|
||||
/// </summary>
|
||||
void LFGMgr::LoadDungeonEncounters()
|
||||
{
|
||||
m_EncountersByAchievement.clear();
|
||||
|
||||
uint32 count = 0;
|
||||
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT achievementId, dungeonId FROM lfg_dungeon_encounters");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
barGoLink bar(1);
|
||||
bar.step();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outErrorDb(">> Loaded 0 dungeon encounter lfg associations. DB table `lfg_dungeon_encounters` is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
barGoLink bar(result->GetRowCount());
|
||||
|
||||
Field* fields = NULL;
|
||||
do
|
||||
{
|
||||
fields = result->Fetch();
|
||||
uint32 achievementId = fields[0].GetUInt32();
|
||||
uint32 dungeonId = fields[1].GetUInt32();
|
||||
|
||||
if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId))
|
||||
{
|
||||
if (!(achievement->flags & ACHIEVEMENT_FLAG_COUNTER))
|
||||
{
|
||||
sLog.outErrorDb("Achievement %u specified in table `lfg_dungeon_encounters` is not a statistic!", achievementId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outErrorDb("Achievement %u specified in table `lfg_dungeon_encounters` does not exist!", achievementId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sLFGDungeonStore.LookupEntry(dungeonId))
|
||||
{
|
||||
sLog.outErrorDb("Dungeon %u specified in table `lfg_dungeon_encounters` does not exist!", dungeonId);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_EncountersByAchievement[achievementId] = dungeonId;
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u dungeon encounter lfg associations.", count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load rewards for completing dungeons
|
||||
/// </summary>
|
||||
void LFGMgr::LoadRewards()
|
||||
{
|
||||
for (LfgRewardMap::iterator itr = m_RewardMap.begin(); itr != m_RewardMap.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_RewardMap.clear();
|
||||
|
||||
uint32 count = 0;
|
||||
// ORDER BY is very important for GetRandomDungeonReward!
|
||||
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT dungeonId, maxLevel, firstQuestId, firstMoneyVar, firstXPVar, otherQuestId, otherMoneyVar, otherXPVar FROM lfg_dungeon_rewards ORDER BY dungeonId, maxLevel ASC");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
barGoLink bar(1);
|
||||
bar.step();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outErrorDb(">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
barGoLink bar(result->GetRowCount());
|
||||
|
||||
Field* fields = NULL;
|
||||
do
|
||||
{
|
||||
fields = result->Fetch();
|
||||
uint32 dungeonId = fields[0].GetUInt32();
|
||||
uint32 maxLevel = fields[1].GetUInt8();
|
||||
uint32 firstQuestId = fields[2].GetUInt32();
|
||||
uint32 firstMoneyVar = fields[3].GetUInt32();
|
||||
uint32 firstXPVar = fields[4].GetUInt32();
|
||||
uint32 otherQuestId = fields[5].GetUInt32();
|
||||
uint32 otherMoneyVar = fields[6].GetUInt32();
|
||||
uint32 otherXPVar = fields[7].GetUInt32();
|
||||
|
||||
if (!sLFGDungeonStore.LookupEntry(dungeonId))
|
||||
{
|
||||
sLog.outErrorDb("Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!maxLevel || maxLevel > sWorld.getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
{
|
||||
sLog.outErrorDb("Level %u specified for dungeon %u in table `lfg_dungeon_rewards` can never be reached!", maxLevel, dungeonId);
|
||||
maxLevel = sWorld.getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
}
|
||||
|
||||
if (firstQuestId && !sObjectMgr.GetQuestTemplate(firstQuestId))
|
||||
{
|
||||
sLog.outErrorDb("First quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", firstQuestId, dungeonId);
|
||||
firstQuestId = 0;
|
||||
}
|
||||
|
||||
if (otherQuestId && !sObjectMgr.GetQuestTemplate(otherQuestId))
|
||||
{
|
||||
sLog.outErrorDb("Other quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", otherQuestId, dungeonId);
|
||||
otherQuestId = 0;
|
||||
}
|
||||
|
||||
m_RewardMap.insert(LfgRewardMap::value_type(dungeonId, new LfgReward(maxLevel, firstQuestId, firstMoneyVar, firstXPVar, otherQuestId, otherMoneyVar, otherXPVar)));
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u lfg dungeon rewards.", count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the player/group to lfg queue
|
||||
/// </summary>
|
||||
@@ -1566,24 +1661,47 @@ void LFGMgr::SendLfgPlayerInfo(Player *plr)
|
||||
data << uint8(0);
|
||||
else
|
||||
{
|
||||
LfgReward *reward;
|
||||
LfgReward const* reward = NULL;
|
||||
Quest const* qRew = NULL;
|
||||
|
||||
data << uint8(randomlist->size()); // Random Dungeon count
|
||||
for (LfgDungeonSet::iterator it = randomlist->begin(); it != randomlist->end(); ++it)
|
||||
{
|
||||
done = plr->isLfgDungeonDone(*it);
|
||||
reward = GetRandomDungeonReward(*it, done, plr->getLevel());
|
||||
reward = GetRandomDungeonReward(*it, plr->getLevel());
|
||||
data << uint32(*it); // Entry
|
||||
data << uint8(done);
|
||||
data << uint32(reward->baseMoney);
|
||||
data << uint32(reward->baseXP);
|
||||
data << uint32(reward->variableMoney);
|
||||
data << uint32(reward->variableXP);
|
||||
data << uint8(reward->itemId != 0);
|
||||
if (reward->itemId)
|
||||
if (reward)
|
||||
{
|
||||
data << uint32(reward->itemId);
|
||||
data << uint32(reward->displayId);
|
||||
data << uint32(reward->stackCount);
|
||||
qRew = sObjectMgr.GetQuestTemplate(reward->reward[done].questId);
|
||||
data << uint32(qRew ? qRew->GetRewOrReqMoney() : 0);
|
||||
data << uint32(qRew ? qRew->XPValue(plr) : 0);
|
||||
data << uint32(reward->reward[done].variableMoney);
|
||||
data << uint32(reward->reward[done].variableXP);
|
||||
data << uint8(qRew ? qRew->GetRewItemsCount() : 0);
|
||||
if (qRew && qRew->GetRewItemsCount())
|
||||
{
|
||||
ItemPrototype const* iProto = NULL;
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
if (!qRew->RewItemId[i])
|
||||
continue;
|
||||
|
||||
iProto = ObjectMgr::GetItemPrototype(qRew->RewItemId[i]);
|
||||
|
||||
data << uint32(qRew->RewItemId[i]);
|
||||
data << uint32(iProto ? iProto->DisplayInfoID : 0);
|
||||
data << uint32(qRew->RewItemCount[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint8(0);
|
||||
}
|
||||
}
|
||||
randomlist->clear();
|
||||
@@ -1661,29 +1779,55 @@ void LFGMgr::SendLfgPlayerReward(Player *plr)
|
||||
if (plr->GetGroup())
|
||||
sdungeonId = plr->GetGroup()->GetLfgDungeonEntry(false);
|
||||
bool done = plr->isLfgDungeonDone(rdungeonId);
|
||||
LfgReward *reward = GetRandomDungeonReward(rdungeonId, done, plr->getLevel());
|
||||
if (!reward)
|
||||
return;
|
||||
LfgReward const* reward = GetRandomDungeonReward(rdungeonId, plr->getLevel());
|
||||
|
||||
uint8 itemNum = uint8(reward->itemId != 0);
|
||||
uint8 itemNum = 0;
|
||||
Quest const* qRew = NULL;
|
||||
if (reward)
|
||||
{
|
||||
qRew = sObjectMgr.GetQuestTemplate(reward->reward[done].questId);
|
||||
if (qRew)
|
||||
itemNum = qRew->GetRewItemsCount();
|
||||
}
|
||||
|
||||
sLog.outDebug("SMSG_LFG_PLAYER_REWARD");
|
||||
WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4));
|
||||
data << uint32(rdungeonId); // Random Dungeon Finished
|
||||
data << uint32(sdungeonId); // Dungeon Finished
|
||||
data << uint8(done);
|
||||
data << uint32(reward->strangers);
|
||||
data << uint32(reward->baseMoney);
|
||||
data << uint32(reward->baseXP);
|
||||
data << uint32(reward->variableMoney);
|
||||
data << uint32(reward->variableXP);
|
||||
data << uint8(itemNum);
|
||||
if (itemNum)
|
||||
data << uint32(reward ? 1 : 0);
|
||||
if (reward)
|
||||
{
|
||||
data << uint32(reward->itemId);
|
||||
data << uint32(reward->displayId);
|
||||
data << uint32(reward->stackCount);
|
||||
data << uint32(qRew ? qRew->GetRewOrReqMoney() : 0);
|
||||
data << uint32(qRew ? qRew->XPValue(plr) : 0);
|
||||
data << uint32(reward->reward[done].variableMoney);
|
||||
data << uint32(reward->reward[done].variableXP);
|
||||
data << uint8(itemNum);
|
||||
if (qRew && itemNum)
|
||||
{
|
||||
ItemPrototype const* iProto = NULL;
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
if (!qRew->RewItemId[i])
|
||||
continue;
|
||||
|
||||
iProto = ObjectMgr::GetItemPrototype(qRew->RewItemId[i]);
|
||||
|
||||
data << uint32(qRew->RewItemId[i]);
|
||||
data << uint32(iProto ? iProto->DisplayInfoID : 0);
|
||||
data << uint32(qRew->RewItemCount[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint8(0);
|
||||
}
|
||||
|
||||
plr->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
@@ -1733,6 +1877,65 @@ void LFGMgr::BuildPlayerLockDungeonBlock(WorldPacket &data, LfgLockStatusSet *lo
|
||||
delete lockSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Give completion reward to player
|
||||
/// </summary>
|
||||
/// <param name="const uint32">dungeonId</param>
|
||||
/// <param name="Player *">player</param>
|
||||
void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player *player)
|
||||
{
|
||||
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS, 1);
|
||||
|
||||
if (isRandomDungeon(dungeonId))
|
||||
player->SetLfgDungeonDone(dungeonId);
|
||||
|
||||
LfgReward const* reward = GetRandomDungeonReward(dungeonId, player->getLevel());
|
||||
if (!reward)
|
||||
return;
|
||||
|
||||
uint8 index = player->isLfgDungeonDone(dungeonId) ? 1 : 0;
|
||||
Quest const* qReward = sObjectMgr.GetQuestTemplate(reward->reward[index].questId);
|
||||
if (!qReward)
|
||||
return;
|
||||
|
||||
if (qReward->GetRewItemsCount() > 0)
|
||||
{
|
||||
for (uint32 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
if (uint32 itemId = qReward->RewItemId[i])
|
||||
{
|
||||
ItemPosCountVec dest;
|
||||
if (player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, qReward->RewItemCount[i]) == EQUIP_ERR_OK)
|
||||
{
|
||||
Item* item = player->StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
|
||||
player->SendNewItem(item, qReward->RewItemCount[i], true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not give XP in case already completed once repeatable quest
|
||||
uint32 XP = qReward->XPValue(player) * sWorld.getRate(RATE_XP_QUEST);
|
||||
|
||||
Group* group = player->GetGroup();
|
||||
if (group)
|
||||
XP += (5 - group->GetMembersCount()) * reward->reward[index].variableXP;
|
||||
|
||||
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
|
||||
int32 moneyRew = qReward->GetRewOrReqMoney();
|
||||
|
||||
if (player->getLevel() < sWorld.getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
player->GiveXP(XP, NULL);
|
||||
else
|
||||
moneyRew += int32(qReward->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY));
|
||||
|
||||
if (group)
|
||||
moneyRew += (5 - group->GetMembersCount()) * reward->reward[index].variableMoney;
|
||||
|
||||
if (moneyRew)
|
||||
player->ModifyMoney(moneyRew);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------//
|
||||
// Auxiliar Functions
|
||||
// --------------------------------------------------------------------------//
|
||||
@@ -1945,47 +2148,22 @@ LfgDungeonSet* LFGMgr::GetRandomDungeons(uint8 level, uint8 expansion)
|
||||
/// Get the reward of a given random dungeon at a certain level
|
||||
/// </summary>
|
||||
/// <param name="uint32">random dungeon id</param>
|
||||
/// <param name="bool">Dungeon previously done</param>
|
||||
/// <param name="uint8">Player level</param>
|
||||
/// <returns>LfgReward*</returns>
|
||||
LfgReward* LFGMgr::GetRandomDungeonReward(uint32 dungeon, bool done, uint8 level)
|
||||
/// <returns>LfgReward const*</returns>
|
||||
LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level)
|
||||
{
|
||||
LfgReward const* rew = NULL;
|
||||
LfgRewardMapBounds bounds = m_RewardMap.equal_range(dungeon & 0x00FFFFFF);
|
||||
uint8 index = 0;
|
||||
switch((dungeon & 0x00FFFFFF)) // Get dungeon id from dungeon entry
|
||||
for (LfgRewardMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
|
||||
{
|
||||
case LFG_RANDOM_CLASSIC:
|
||||
if (level < 15)
|
||||
index = LFG_REWARD_LEVEL0;
|
||||
else if (level < 24)
|
||||
index = LFG_REWARD_LEVEL1;
|
||||
else if (level < 35)
|
||||
index = LFG_REWARD_LEVEL2;
|
||||
else if (level < 46)
|
||||
index = LFG_REWARD_LEVEL3;
|
||||
else if (level < 56)
|
||||
index = LFG_REWARD_LEVEL4;
|
||||
else
|
||||
index = LFG_REWARD_LEVEL5;
|
||||
break;
|
||||
case LFG_RANDOM_BC_NORMAL:
|
||||
index = LFG_REWARD_BC_NORMAL;
|
||||
break;
|
||||
case LFG_RANDOM_BC_HEROIC:
|
||||
index = LFG_REWARD_BC_HEROIC;
|
||||
break;
|
||||
case LFG_RANDOM_LK_NORMAL:
|
||||
index = level == 80 ? LFG_REWARD_LK_NORMAL80 : LFG_REWARD_LK_NORMAL;
|
||||
break;
|
||||
case LFG_RANDOM_LK_HEROIC:
|
||||
index = LFG_REWARD_LK_HEROIC;
|
||||
break;
|
||||
default: // This should never happen!
|
||||
done = false;
|
||||
index = LFG_REWARD_LEVEL0;
|
||||
sLog.outError("LFGMgr::GetRandomDungeonReward: Dungeon %u is not random dungeon!", dungeon);
|
||||
break;
|
||||
rew = itr->second;
|
||||
// ordered properly at loading
|
||||
if (itr->second->maxLevel >= level)
|
||||
break;
|
||||
}
|
||||
return done ? m_RewardDoneList.at(index) : m_RewardList.at(index);
|
||||
|
||||
return rew;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -131,62 +131,6 @@ enum LfgRoleCheckResult
|
||||
LFG_ROLECHECK_NO_ROLE = 6, // Someone selected no role
|
||||
};
|
||||
|
||||
// TODO: Remove me, will not be needed when Reward moved to quest system
|
||||
enum LfgRandomDungeonEntries
|
||||
{
|
||||
LFG_ALL_DUNGEONS = 0,
|
||||
LFG_RANDOM_CLASSIC = 258,
|
||||
LFG_RANDOM_BC_NORMAL = 259,
|
||||
LFG_RANDOM_BC_HEROIC = 260,
|
||||
LFG_RANDOM_LK_NORMAL = 261,
|
||||
LFG_RANDOM_LK_HEROIC = 262,
|
||||
};
|
||||
|
||||
enum LfgRewardEnums
|
||||
{
|
||||
LFG_REWARD_LEVEL0 = 10,
|
||||
LFG_REWARD_LEVEL1 = 0,
|
||||
LFG_REWARD_LEVEL2 = 1,
|
||||
LFG_REWARD_LEVEL3 = 2,
|
||||
LFG_REWARD_LEVEL4 = 3,
|
||||
LFG_REWARD_LEVEL5 = 4,
|
||||
LFG_REWARD_BC_NORMAL = 5,
|
||||
LFG_REWARD_BC_HEROIC = 6,
|
||||
LFG_REWARD_LK_NORMAL = 7,
|
||||
LFG_REWARD_LK_NORMAL80 = 7,
|
||||
LFG_REWARD_LK_HEROIC = 8,
|
||||
LFG_REWARD_DATA_SIZE = 10,
|
||||
};
|
||||
|
||||
const uint32 RewardDungeonData[LFG_REWARD_DATA_SIZE+1][5] =
|
||||
{ // XP, money, item, item display, count
|
||||
{310, 3500, 51999, 56915, 1}, // Classic 15-23
|
||||
{470, 7000, 52000, 56915, 1}, // Classic 24-34
|
||||
{825, 13000, 52001, 56915, 1}, // Classic 35-45
|
||||
{12250, 16500, 52002, 56915, 1}, // Classic 46-55
|
||||
{14300, 18000, 52003, 56915, 1}, // Classic 56-60
|
||||
{1600, 62000, 52004, 56915, 1}, // BC Normal
|
||||
{1900, 88000, 52005, 56915, 1}, // BC Heroic
|
||||
{33100, 148000, 47241, 62232, 2}, // LK Normal
|
||||
{0, 198600, 47241, 62232, 2}, // LK Normal - Level 80
|
||||
{0, 264600, 49426, 64062, 2}, // LK Heroic
|
||||
{0, 0, 0, 0, 0}, // Classic - No level
|
||||
};
|
||||
|
||||
const uint32 RewardDungeonDoneData[LFG_REWARD_DATA_SIZE][5] =
|
||||
{ // XP, money, item, item display, count
|
||||
{200, 1800, 51999, 56915, 1}, // Classic 15-23
|
||||
{310, 3500, 52000, 56915, 1}, // Classic 24-34
|
||||
{550, 6500, 52001, 56915, 1}, // Classic 35-45
|
||||
{8150, 8500, 52002, 56915, 1}, // Classic 46-55
|
||||
{9550, 9000, 52003, 56915, 1}, // Classic 56-60
|
||||
{1100, 31000, 52004, 56915, 1}, // BC Normal
|
||||
{12650, 44000, 52005, 56915, 1}, // BC Heroic
|
||||
{16550, 74000, 0, 0, 0}, // LK Normal
|
||||
{0, 99300, 0, 0, 0}, // LK Normal - Level 80
|
||||
{0, 132300, 47241, 62232, 2}, // LK Heroic
|
||||
};
|
||||
|
||||
// Dungeon and reason why player can't join
|
||||
struct LfgLockStatus
|
||||
{
|
||||
@@ -197,14 +141,24 @@ struct LfgLockStatus
|
||||
// Reward info
|
||||
struct LfgReward
|
||||
{
|
||||
uint32 strangers;
|
||||
uint32 baseMoney;
|
||||
uint32 baseXP;
|
||||
uint32 variableMoney;
|
||||
uint32 variableXP;
|
||||
uint32 itemId;
|
||||
uint32 displayId;
|
||||
uint32 stackCount;
|
||||
uint32 maxLevel;
|
||||
struct
|
||||
{
|
||||
uint32 questId;
|
||||
uint32 variableMoney;
|
||||
uint32 variableXP;
|
||||
} reward[2];
|
||||
|
||||
LfgReward(uint32 _maxLevel, uint32 firstQuest, uint32 firstVarMoney, uint32 firstVarXp, uint32 otherQuest, uint32 otherVarMoney, uint32 otherVarXp)
|
||||
: maxLevel(_maxLevel)
|
||||
{
|
||||
reward[0].questId = firstQuest;
|
||||
reward[0].variableMoney = firstVarMoney;
|
||||
reward[0].variableXP = firstVarXp;
|
||||
reward[1].questId = otherQuest;
|
||||
reward[1].variableMoney = otherVarMoney;
|
||||
reward[1].variableXP = otherVarXp;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<uint32, uint8> LfgRolesMap;
|
||||
@@ -279,14 +233,14 @@ struct LfgPlayerBoot
|
||||
|
||||
typedef std::set<Player*> PlayerSet;
|
||||
typedef std::set<LfgLockStatus*> LfgLockStatusSet;
|
||||
typedef std::vector<LfgReward*> LfgRewardList;
|
||||
typedef std::map<uint32, LfgReward*> LfgRewardMap;
|
||||
typedef std::vector<LfgProposal*> LfgProposalList;
|
||||
typedef std::map<uint32, LfgLockStatusSet*> LfgLockStatusMap;
|
||||
typedef std::map<uint64, LfgQueueInfo*> LfgQueueInfoMap;
|
||||
typedef std::map<uint32, LfgRoleCheck*> LfgRoleCheckMap;
|
||||
typedef std::map<uint32, LfgProposal*> LfgProposalMap;
|
||||
typedef std::map<uint32, LfgPlayerBoot*> LfgPlayerBootMap;
|
||||
typedef std::multimap<uint32, LfgReward const*> LfgRewardMap;
|
||||
typedef std::pair<LfgRewardMap::const_iterator, LfgRewardMap::const_iterator> LfgRewardMapBounds;
|
||||
typedef std::list<Player *> LfgPlayerList;
|
||||
|
||||
class LFGMgr
|
||||
@@ -311,6 +265,18 @@ class LFGMgr
|
||||
bool isRandomDungeon(uint32 dungeonId);
|
||||
void InitBoot(Group *grp, uint32 plowGuid, uint32 vlowGuid, std::string reason);
|
||||
|
||||
void LoadDungeonEncounters();
|
||||
void LoadRewards();
|
||||
void RewardDungeonDoneFor(const uint32 dungeonId, Player* player);
|
||||
const uint32 GetDungeonIdForAchievement(uint32 achievementId)
|
||||
{
|
||||
std::map<uint32, uint32>::iterator itr = m_EncountersByAchievement.find(achievementId);
|
||||
if (itr != m_EncountersByAchievement.end())
|
||||
return itr->second;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Cleaner();
|
||||
void AddGuidToNewQueue(uint64 guid);
|
||||
@@ -337,11 +303,11 @@ class LFGMgr
|
||||
LfgDungeonSet* GetDungeonsByRandom(uint32 randomdungeon);
|
||||
LfgDungeonSet* GetRandomDungeons(uint8 level, uint8 expansion);
|
||||
LfgDungeonSet* GetAllDungeons();
|
||||
LfgReward* GetRandomDungeonReward(uint32 dungeon, bool done, uint8 level);
|
||||
LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level);
|
||||
uint8 GetDungeonGroupType(uint32 dungeon);
|
||||
|
||||
LfgRewardList m_RewardList; // TODO: Change it to list of quests
|
||||
LfgRewardList m_RewardDoneList; // TODO: Change it to list of quests
|
||||
LfgRewardMap m_RewardMap; // Stores rewards for random dungeons
|
||||
std::map<uint32, uint32> m_EncountersByAchievement; // Stores dungeon ids associated with achievements (for rewards)
|
||||
LfgDungeonMap m_CachedDungeonMap; // Stores all dungeons by groupType
|
||||
LfgQueueInfoMap m_QueueInfoMap; // Queued groups
|
||||
LfgGuidList m_currentQueue; // Ordered list. Used to find groups
|
||||
|
||||
@@ -14373,22 +14373,22 @@ void Player::RewardQuest(Quest const *pQuest, uint32 reward, Object* questGiver,
|
||||
for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
|
||||
XP = uint32(XP*(1.0f + (*i)->GetAmount() / 100.0f));
|
||||
|
||||
int32 moneyRew = 0;
|
||||
if (getLevel() < sWorld.getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
GiveXP(XP, NULL);
|
||||
else
|
||||
{
|
||||
uint32 money = uint32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY));
|
||||
ModifyMoney(money);
|
||||
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money);
|
||||
}
|
||||
moneyRew = int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY));
|
||||
|
||||
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
|
||||
if (pQuest->GetRewOrReqMoney())
|
||||
{
|
||||
ModifyMoney(pQuest->GetRewOrReqMoney());
|
||||
moneyRew += pQuest->GetRewOrReqMoney();
|
||||
|
||||
if (pQuest->GetRewOrReqMoney() > 0)
|
||||
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney());
|
||||
if (moneyRew)
|
||||
{
|
||||
ModifyMoney(moneyRew);
|
||||
|
||||
if (moneyRew > 0)
|
||||
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, uint32(moneyRew));
|
||||
}
|
||||
|
||||
// honor reward
|
||||
|
||||
@@ -2251,6 +2251,7 @@ class Player : public Unit, public GridObject<Player>
|
||||
bool isLfgDungeonDone(const uint32 entry) { return m_LookingForGroup.donerandomDungeons.find(entry) != m_LookingForGroup.donerandomDungeons.end(); }
|
||||
LfgDungeonSet *GetLfgDungeons() { return &m_LookingForGroup.applyDungeons; }
|
||||
LfgDungeonSet *GetLfgDungeonsDone() { return &m_LookingForGroup.donerandomDungeons; }
|
||||
void SetLfgDungeonDone(const uint32 entry) { m_LookingForGroup.donerandomDungeons.insert(entry); }
|
||||
std::string GetLfgComment() { return m_LookingForGroup.comment; }
|
||||
void SetLfgComment(std::string _comment) { m_LookingForGroup.comment = _comment; }
|
||||
uint8 GetLfgRoles() { return m_LookingForGroup.roles; }
|
||||
|
||||
@@ -1431,6 +1431,12 @@ void World::SetInitialWorldSettings()
|
||||
sLog.outString("Loading Quests Relations...");
|
||||
sObjectMgr.LoadQuestRelations(); // must be after quest load
|
||||
|
||||
sLog.outString("Loading Dungeon boss data...");
|
||||
sLFGMgr.LoadDungeonEncounters();
|
||||
|
||||
sLog.outString("Loading LFG rewards...");
|
||||
sLFGMgr.LoadRewards();
|
||||
|
||||
sLog.outString("Loading UNIT_NPC_FLAG_SPELLCLICK Data...");
|
||||
sObjectMgr.LoadNPCSpellClickSpells();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user