diff --git a/Source/Framework/Constants/QuestConst.cs b/Source/Framework/Constants/QuestConst.cs
index 36faeaa04..bd02b1b96 100644
--- a/Source/Framework/Constants/QuestConst.cs
+++ b/Source/Framework/Constants/QuestConst.cs
@@ -230,23 +230,32 @@ namespace Framework.Constants
Max
}
- public enum QuestGiverStatus
+ public enum QuestGiverStatus : uint
{
- None = 0x000,
- Unk = 0x001,
- Unavailable = 0x002,
- LowLevelAvailable = 0x004,
- LowLevelRewardRep = 0x008,
- LowLevelAvailableRep = 0x010,
- Incomplete = 0x020,
- RewardRep = 0x040,
- AvailableRep = 0x080,
- Available = 0x100,
- Reward2 = 0x200, // No Yellow Dot On Minimap
- Reward = 0x400, // Yellow Dot On Minimap
+ None = 0x00,
+ Future = 0x02,
+ Trivial = 0x04,
+ TrivialRepeatableTurnin = 0x08,
+ TrivialDailyQuest = 0x10,
+ Reward = 0x20,
+ JourneyReward = 0x40,
+ CovenantCallingReward = 0x80,
+ RepeatableTurnin = 0x100,
+ DailyQuest = 0x200,
+ Quest = 0x400,
+ RewardCompleteNoPOI = 0x800,
+ RewardCompletePOI = 0x1000,
+ LegendaryQuest = 0x2000,
+ LegendaryRewardCompleteNoPOI = 0x4000,
+ LegendaryRewardCompletePOI = 0x8000,
+ JourneyQuest = 0x10000,
+ JourneyRewardCompleteNoPOI = 0x20000,
+ JourneyRewardCompletePOI = 0x40000,
+ CovenantCallingQuest = 0x80000,
+ CovenantCallingRewardCompleteNoPOI = 0x100000,
+ CovenantCallingRewardCompletePOI = 0x200000,
- // Custom value meaning that script call did not return any valid quest status
- ScriptedNoStatus = 0x1000
+ ScriptedDefault = 0x80000000
}
[Flags]
@@ -373,4 +382,24 @@ namespace Framework.Constants
Delete,
ForceDelete
}
+
+ public enum QuestTagType
+ {
+ Tag,
+ Profession,
+ Normal,
+ Pvp,
+ PetBattle,
+ Bounty,
+ Dungeon,
+ Invasion,
+ Raid,
+ Contribution,
+ RatedRreward,
+ InvasionWrapper,
+ FactionAssault,
+ Islands,
+ Threat,
+ CovenantCalling
+ }
}
diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs
index 59e01b052..9605c49ed 100644
--- a/Source/Framework/Database/Databases/HotfixDatabase.cs
+++ b/Source/Framework/Database/Databases/HotfixDatabase.cs
@@ -790,6 +790,11 @@ namespace Framework.Database
PrepareStatement(HotfixStatements.SEL_QUEST_FACTION_REWARD, "SELECT ID, Difficulty1, Difficulty2, Difficulty3, Difficulty4, Difficulty5, Difficulty6, " +
"Difficulty7, Difficulty8, Difficulty9, Difficulty10 FROM quest_faction_reward");
+ // QuestInfo.db2
+ PrepareStatement(HotfixStatements.SEL_QUEST_INFO, "SELECT ID, InfoName, Type, Modifiers, Profession FROM quest_info");
+ PrepareStatement(HotfixStatements.SEL_QUEST_INFO_LOCALE, "SELECT ID, InfoName_lang FROM quest_info_locale WHERE locale = ?");
+
+
// QuestMoneyReward.db2
PrepareStatement(HotfixStatements.SEL_QUEST_MONEY_REWARD, "SELECT ID, Difficulty1, Difficulty2, Difficulty3, Difficulty4, Difficulty5, Difficulty6, " +
"Difficulty7, Difficulty8, Difficulty9, Difficulty10 FROM quest_money_reward");
@@ -1534,6 +1539,9 @@ namespace Framework.Database
SEL_QUEST_FACTION_REWARD,
+ SEL_QUEST_INFO,
+ SEL_QUEST_INFO_LOCALE,
+
SEL_QUEST_MONEY_REWARD,
SEL_QUEST_PACKAGE_ITEM,
diff --git a/Source/Game/AI/CoreAI/GameObjectAI.cs b/Source/Game/AI/CoreAI/GameObjectAI.cs
index 521702298..c1111f3cb 100644
--- a/Source/Game/AI/CoreAI/GameObjectAI.cs
+++ b/Source/Game/AI/CoreAI/GameObjectAI.cs
@@ -74,7 +74,7 @@ namespace Game.AI
///
/// Called when the dialog status between a player and the gameobject is requested.
///
- public virtual uint GetDialogStatus(Player player) { return 100; }
+ public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedDefault; }
// Called when a Player clicks a GameObject, before GossipHello
// prevents achievement tracking if returning true
diff --git a/Source/Game/AI/CoreAI/UnitAI.cs b/Source/Game/AI/CoreAI/UnitAI.cs
index 803963593..df3882096 100644
--- a/Source/Game/AI/CoreAI/UnitAI.cs
+++ b/Source/Game/AI/CoreAI/UnitAI.cs
@@ -529,7 +529,7 @@ namespace Game.AI
public virtual void OnGameEvent(bool start, ushort eventId) { }
// Called when the dialog status between a player and the creature is requested.
- public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedNoStatus; }
+ public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedDefault; }
public virtual void WaypointStarted(uint nodeId, uint pathId) { }
diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs
index 9dcf3c9f7..bf773c7e5 100644
--- a/Source/Game/AI/SmartScripts/SmartAI.cs
+++ b/Source/Game/AI/SmartScripts/SmartAI.cs
@@ -1079,11 +1079,6 @@ namespace Game.AI
GetScript().ProcessEventsFor(SmartEvents.RewardQuest, player, quest.Id, opt, false, null, me);
}
- public override uint GetDialogStatus(Player player)
- {
- return 100;
- }
-
public override void Destroyed(Player player, uint eventId)
{
GetScript().ProcessEventsFor(SmartEvents.Death, player, eventId, 0, false, null, me);
diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs
index 34fd03dee..28cfa29f3 100644
--- a/Source/Game/DataStorage/CliDB.cs
+++ b/Source/Game/DataStorage/CliDB.cs
@@ -229,6 +229,7 @@ namespace Game.DataStorage
PvpTalentCategoryStorage = ReadDB2("PvpTalentCategory.db2", HotfixStatements.SEL_PVP_TALENT_CATEGORY);
PvpTalentSlotUnlockStorage = ReadDB2("PvpTalentSlotUnlock.db2", HotfixStatements.SEL_PVP_TALENT_SLOT_UNLOCK);
QuestFactionRewardStorage = ReadDB2("QuestFactionReward.db2", HotfixStatements.SEL_QUEST_FACTION_REWARD);
+ QuestInfoStorage = ReadDB2("QuestInfo.db2", HotfixStatements.SEL_QUEST_INFO, HotfixStatements.SEL_QUEST_INFO_LOCALE);
QuestMoneyRewardStorage = ReadDB2("QuestMoneyReward.db2", HotfixStatements.SEL_QUEST_MONEY_REWARD);
QuestPackageItemStorage = ReadDB2("QuestPackageItem.db2", HotfixStatements.SEL_QUEST_PACKAGE_ITEM);
QuestSortStorage = ReadDB2("QuestSort.db2", HotfixStatements.SEL_QUEST_SORT, HotfixStatements.SEL_QUEST_SORT_LOCALE);
@@ -584,6 +585,7 @@ namespace Game.DataStorage
public static DB6Storage PvpTalentCategoryStorage;
public static DB6Storage PvpTalentSlotUnlockStorage;
public static DB6Storage QuestFactionRewardStorage;
+ public static DB6Storage QuestInfoStorage;
public static DB6Storage QuestMoneyRewardStorage;
public static DB6Storage QuestPackageItemStorage;
public static DB6Storage QuestSortStorage;
diff --git a/Source/Game/DataStorage/Structs/Q_Records.cs b/Source/Game/DataStorage/Structs/Q_Records.cs
index fcd45091e..30a27ac58 100644
--- a/Source/Game/DataStorage/Structs/Q_Records.cs
+++ b/Source/Game/DataStorage/Structs/Q_Records.cs
@@ -25,6 +25,15 @@ namespace Game.DataStorage
public short[] Difficulty = new short[10];
}
+ public sealed class QuestInfoRecord
+ {
+ public uint Id;
+ public LocalizedString InfoName;
+ public sbyte Type;
+ public byte Modifiers;
+ public ushort Profession;
+ }
+
public sealed class QuestMoneyRewardRecord
{
public uint Id;
diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs
index cf44db25b..3edeead2b 100644
--- a/Source/Game/Entities/GameObject/GameObject.cs
+++ b/Source/Game/Entities/GameObject/GameObject.cs
@@ -1217,7 +1217,7 @@ namespace Game.Entities
{
case GameObjectTypes.QuestGiver:
QuestGiverStatus questStatus = target.GetQuestDialogStatus(this);
- if (questStatus > QuestGiverStatus.Unavailable)
+ if (questStatus != QuestGiverStatus.None && questStatus != QuestGiverStatus.Future)
return true;
break;
// scan GO chest with loot including quest items
diff --git a/Source/Game/Entities/Player/Player.Quest.cs b/Source/Game/Entities/Player/Player.Quest.cs
index f487750f4..98cf9dc44 100644
--- a/Source/Game/Entities/Player/Player.Quest.cs
+++ b/Source/Game/Entities/Player/Player.Quest.cs
@@ -1860,8 +1860,8 @@ namespace Game.Entities
{
case TypeId.GameObject:
{
- QuestGiverStatus questStatus = (QuestGiverStatus)questgiver.ToGameObject().GetAI().GetDialogStatus(this);
- if (questStatus != QuestGiverStatus.ScriptedNoStatus)
+ QuestGiverStatus questStatus = questgiver.ToGameObject().GetAI().GetDialogStatus(this);
+ if (questStatus != QuestGiverStatus.ScriptedDefault)
return questStatus;
qr = Global.ObjectMgr.GetGOQuestRelationBounds(questgiver.GetEntry());
qir = Global.ObjectMgr.GetGOQuestInvolvedRelationBounds(questgiver.GetEntry());
@@ -1869,8 +1869,8 @@ namespace Game.Entities
}
case TypeId.Unit:
{
- QuestGiverStatus questStatus = (QuestGiverStatus)questgiver.ToCreature().GetAI().GetDialogStatus(this);
- if (questStatus != QuestGiverStatus.ScriptedNoStatus)
+ QuestGiverStatus questStatus = questgiver.ToCreature().GetAI().GetDialogStatus(this);
+ if (questStatus != QuestGiverStatus.ScriptedDefault)
return questStatus;
qr = Global.ObjectMgr.GetCreatureQuestRelationBounds(questgiver.GetEntry());
qir = Global.ObjectMgr.GetCreatureQuestInvolvedRelationBounds(questgiver.GetEntry());
@@ -1886,27 +1886,41 @@ namespace Game.Entities
foreach (var questId in qir)
{
- QuestGiverStatus result2 = QuestGiverStatus.None;
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
if (quest == null)
continue;
- QuestStatus status = GetQuestStatus(questId);
- if (status == QuestStatus.Complete && !GetQuestRewardStatus(questId))
- result2 = QuestGiverStatus.Reward;
- else if (status == QuestStatus.Incomplete)
- result2 = QuestGiverStatus.Incomplete;
+ switch (GetQuestStatus(questId))
+ {
+ case QuestStatus.Complete:
+ if (quest.GetQuestTag() == QuestTagType.CovenantCalling)
+ result |= quest.HasFlag(QuestFlags.HideRewardPoi) ? QuestGiverStatus.CovenantCallingRewardCompleteNoPOI : QuestGiverStatus.CovenantCallingRewardCompletePOI;
+ else if (quest.HasFlagEx(QuestFlagsEx.LegendaryQuest))
+ result |= quest.HasFlag(QuestFlags.HideRewardPoi) ? QuestGiverStatus.LegendaryRewardCompleteNoPOI : QuestGiverStatus.LegendaryRewardCompletePOI;
+ else
+ result |= quest.HasFlag(QuestFlags.HideRewardPoi) ? QuestGiverStatus.RewardCompleteNoPOI : QuestGiverStatus.RewardCompletePOI;
+ break;
+ case QuestStatus.Incomplete:
+ if (quest.GetQuestTag() == QuestTagType.CovenantCalling)
+ result |= QuestGiverStatus.CovenantCallingReward;
+ else
+ result |= QuestGiverStatus.Reward;
+ break;
+ default:
+ break;
+ }
if (quest.IsAutoComplete() && CanTakeQuest(quest, false) && quest.IsRepeatable() && !quest.IsDailyOrWeekly())
- result2 = QuestGiverStatus.RewardRep;
-
- if (result2 > result)
- result = result2;
+ {
+ if (GetLevel() <= (GetQuestLevel(quest) + WorldConfig.GetIntValue(WorldCfg.QuestLowLevelHideDiff)))
+ result |= QuestGiverStatus.RepeatableTurnin;
+ else
+ result |= QuestGiverStatus.TrivialRepeatableTurnin;
+ }
}
foreach (var questId in qr)
{
- QuestGiverStatus result2 = QuestGiverStatus.None;
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
if (quest == null)
continue;
@@ -1914,8 +1928,7 @@ namespace Game.Entities
if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAvailable, quest.Id, this))
continue;
- QuestStatus status = GetQuestStatus(questId);
- if (status == QuestStatus.None)
+ if (GetQuestStatus(questId) == QuestStatus.None)
{
if (CanSeeStartQuest(quest))
{
@@ -1923,21 +1936,24 @@ namespace Game.Entities
{
if (GetLevel() <= (GetQuestLevel(quest) + WorldConfig.GetIntValue(WorldCfg.QuestLowLevelHideDiff)))
{
- if (quest.IsDaily())
- result2 = QuestGiverStatus.AvailableRep;
+ if (quest.GetQuestTag() == QuestTagType.CovenantCalling)
+ result |= QuestGiverStatus.CovenantCallingQuest;
+ else if (quest.HasFlagEx(QuestFlagsEx.LegendaryQuest))
+ result |= QuestGiverStatus.LegendaryQuest;
+ else if (quest.IsDaily())
+ result |= QuestGiverStatus.DailyQuest;
else
- result2 = QuestGiverStatus.Available;
+ result |= QuestGiverStatus.Quest;
}
+ else if (quest.IsDaily())
+ result |= QuestGiverStatus.TrivialDailyQuest;
else
- result2 = QuestGiverStatus.LowLevelAvailable;
+ result |= QuestGiverStatus.Trivial;
}
else
- result2 = QuestGiverStatus.Unavailable;
+ result |= QuestGiverStatus.Future;
}
}
-
- if (result2 > result)
- result = result2;
}
return result;
diff --git a/Source/Game/Quest/Quest.cs b/Source/Game/Quest/Quest.cs
index fabd95391..ce0e052b9 100644
--- a/Source/Game/Quest/Quest.cs
+++ b/Source/Game/Quest/Quest.cs
@@ -318,6 +318,15 @@ namespace Game
return 0;
}
+ public QuestTagType? GetQuestTag()
+ {
+ QuestInfoRecord questInfo = CliDB.QuestInfoStorage.LookupByKey(QuestInfoID);
+ if (questInfo != null)
+ return (QuestTagType)questInfo.Type;
+
+ return null;
+ }
+
public void BuildQuestRewards(QuestRewards rewards, Player player)
{
rewards.ChoiceItemCount = GetRewChoiceItemsCount();
diff --git a/sql/updates/hotfixes/master/2020_12_11_00_hotfixes.sql b/sql/updates/hotfixes/master/2020_12_11_00_hotfixes.sql
new file mode 100644
index 000000000..8fb38de49
--- /dev/null
+++ b/sql/updates/hotfixes/master/2020_12_11_00_hotfixes.sql
@@ -0,0 +1,62 @@
+--
+-- Table structure for table `quest_info`
+--
+
+DROP TABLE IF EXISTS `quest_info`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `quest_info` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `InfoName` text,
+ `Type` tinyint(4) NOT NULL DEFAULT '0',
+ `Modifiers` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `Profession` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `quest_info`
+--
+
+LOCK TABLES `quest_info` WRITE;
+/*!40000 ALTER TABLE `quest_info` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quest_info` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `quest_info_locale`
+--
+
+DROP TABLE IF EXISTS `quest_info_locale`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `quest_info_locale` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `locale` varchar(4) NOT NULL,
+ `InfoName_lang` text,
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
+/*!50500 PARTITION BY LIST COLUMNS(locale)
+(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
+ PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
+ PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
+ PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
+ PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
+ PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
+ PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
+ PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
+ PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
+ PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB) */;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `quest_info_locale`
+--
+
+LOCK TABLES `quest_info_locale` WRITE;
+/*!40000 ALTER TABLE `quest_info_locale` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quest_info_locale` ENABLE KEYS */;
+UNLOCK TABLES;