diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 605b3205a..2bc953cd9 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -1372,7 +1372,6 @@ namespace Framework.Constants TalentsInspecting, ThreatRadius, TradeLevelReq, - UiQuestLevelsInDialogs, // Should We Add Quest Levels To The Title In The Npc Dialogs? UptimeUpdate, VmapIndoorCheck, WardenClientBanDuration, diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index c41fa12ae..46e16861a 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -75,7 +75,7 @@ namespace Framework.Database PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?"); PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, permission, help FROM command"); - PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, FadeRegionRadius, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ? OR 1 = ?"); + PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ? OR 1 = ?"); PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?"); PrepareStatement(WorldStatements.SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?"); PrepareStatement(WorldStatements.SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_"); diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index d2b501bf4..9291d0ca4 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -85,7 +85,6 @@ namespace Game.Entities public float ModExperience; public bool RacialLeader; public uint MovementId; - public float FadeRegionRadius; public int WidgetSetID; public int WidgetSetUnitConditionID; public bool RegenHealth; @@ -266,7 +265,6 @@ namespace Game.Entities stats.HealthScalingExpansion = HealthScalingExpansion; stats.VignetteID = VignetteID; stats.Class = (int)UnitClass; - stats.FadeRegionRadius = FadeRegionRadius; stats.WidgetSetID = WidgetSetID; stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID; diff --git a/Source/Game/Entities/Creature/Gossip.cs b/Source/Game/Entities/Creature/Gossip.cs index 5d3d2668b..9ad4ba609 100644 --- a/Source/Game/Entities/Creature/Gossip.cs +++ b/Source/Game/Entities/Creature/Gossip.cs @@ -274,11 +274,11 @@ namespace Game.Misc if (quest != null) { ClientGossipText text = new ClientGossipText(); - text.QuestID = (int)questID; + text.QuestID = questID; text.ContentTuningID = quest.ContentTuningId; text.QuestType = item.QuestIcon; - text.QuestFlags = (int)quest.Flags; - text.QuestFlagsEx = (int)quest.FlagsEx; + text.QuestFlags = (uint)quest.Flags; + text.QuestFlagsEx = (uint)quest.FlagsEx; text.Repeatable = quest.IsRepeatable(); text.QuestTitle = quest.LogTitle; diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 95ac16729..3f07010bf 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -1865,13 +1865,12 @@ namespace Game creature.ModExperience = fields.Read(68); creature.RacialLeader = fields.Read(69); creature.MovementId = fields.Read(70); - creature.FadeRegionRadius = fields.Read(71); - creature.WidgetSetID = fields.Read(72); - creature.WidgetSetUnitConditionID = fields.Read(73); - creature.RegenHealth = fields.Read(74); - creature.MechanicImmuneMask = fields.Read(75); - creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(76); - creature.ScriptID = GetScriptId(fields.Read(77)); + creature.WidgetSetID = fields.Read(71); + creature.WidgetSetUnitConditionID = fields.Read(72); + creature.RegenHealth = fields.Read(73); + creature.MechanicImmuneMask = fields.Read(74); + creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(75); + creature.ScriptID = GetScriptId(fields.Read(76)); _creatureTemplateStorage.Add(entry, creature); } @@ -6133,6 +6132,7 @@ namespace Game info.stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0)); info.stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0)); info.stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0)); + break; } } } @@ -6502,35 +6502,35 @@ namespace Game _exclusiveQuestGroups.Clear(); SQLResult result = DB.World.Query("SELECT " + - //0 1 2 3 4 5 6 7 8 9 10 11 12 - "ID, QuestType, QuestLevel, ScalingFactionGroup, MaxScalingLevel, QuestPackageID, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, RewardNextQuest, RewardXPDifficulty, RewardXPMultiplier, " + - //13 14 15 16 17 18 19 20 21 22 23 - "RewardMoney, RewardMoneyDifficulty, RewardMoneyMultiplier, RewardBonusMoney, RewardDisplaySpell1, RewardDisplaySpell2, RewardDisplaySpell3, RewardSpell, RewardHonor, RewardKillHonor, StartItem, " + - //24 25 26 27 28 29 + //0 1 2 3 4 5 6 7 8 9 + "ID, QuestType, QuestPackageID, ContentTuningID, QuestSortID, QuestInfoID, SuggestedGroupNum, RewardNextQuest, RewardXPDifficulty, RewardXPMultiplier, " + + //10 11 12 13 14 15 16 17 + "RewardMoney, RewardMoneyDifficulty, RewardMoneyMultiplier, RewardBonusMoney, RewardSpell, RewardHonor, RewardKillHonor, StartItem, " + + //18 19 20 21 22 23 "RewardArtifactXPDifficulty, RewardArtifactXPMultiplier, RewardArtifactCategoryID, Flags, FlagsEx, FlagsEx2, " + - //30 31 32 33 34 35 36 37 + //24 25 26 27 28 29 30 31 "RewardItem1, RewardAmount1, ItemDrop1, ItemDropQuantity1, RewardItem2, RewardAmount2, ItemDrop2, ItemDropQuantity2, " + - //38 39 40 41 42 43 44 45 + //32 33 34 35 36 37 38 39 "RewardItem3, RewardAmount3, ItemDrop3, ItemDropQuantity3, RewardItem4, RewardAmount4, ItemDrop4, ItemDropQuantity4, " + - //46 47 48 49 50 51 + //40 41 42 43 44 45 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemDisplayID1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemDisplayID2, " + - //52 53 54 55 56 57 + //46 47 48 49 50 51 "RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemDisplayID3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemDisplayID4, " + - //58 59 60 61 62 63 + //52 53 54 55 56 57 "RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemDisplayID5, RewardChoiceItemID6, RewardChoiceItemQuantity6, RewardChoiceItemDisplayID6, " + - //64 65 66 67 68 69 70 71 72 73 74 + //58 59 60 61 62 63 64 65 66 67 68 "POIContinent, POIx, POIy, POIPriority, RewardTitle, RewardArenaPoints, RewardSkillLineID, RewardNumSkillUps, PortraitGiver, PortraitGiverMount, PortraitTurnIn, " + - //75 76 77 78 79 80 81 82 + //69 70 71 72 73 74 75 76 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionCapIn1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionCapIn2, " + - //83 84 85 86 87 88 89 90 + //77 78 79 80 81 82 83 84 "RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionCapIn3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionCapIn4, " + - //91 92 93 94 95 + //85 86 87 88 89 "RewardFactionID5, RewardFactionValue5, RewardFactionOverride5, RewardFactionCapIn5, RewardFactionFlags, " + - //96 97 98 99 100 101 102 103 + //90 91 92 93 94 95 96 97 "RewardCurrencyID1, RewardCurrencyQty1, RewardCurrencyID2, RewardCurrencyQty2, RewardCurrencyID3, RewardCurrencyQty3, RewardCurrencyID4, RewardCurrencyQty4, " + - //104 105 106 107 108 109 110 111 112 + //98 99 100 101 102 103 104 105 106 "AcceptedSoundKitID, CompleteSoundKitID, AreaGroupID, TimeAllowed, AllowableRaces, TreasurePickerID, Expansion, ManagedWorldStateID, QuestSessionBonus, " + - //113 114 115 116 117 118 119 120 121 + //107 108 109 110 111 112 113 114 115 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog" + " FROM quest_template"); @@ -6550,6 +6550,51 @@ namespace Game } while (result.NextRow()); + + // Load `quest_reward_choice_items` + // 0 1 2 3 4 5 6 + result = DB.World.Query("SELECT QuestID, Type1, Type2, Type3, Type4, Type5, Type6 FROM quest_reward_choice_items"); + if (result.IsEmpty()) + { + Log.outError(LogFilter.ServerLoading, "Loaded 0 quest reward choice items. DB table `quest_reward_choice_items` is empty."); + } + else + { + do + { + uint questId = result.Read(0); + + var quest = _questTemplates.LookupByKey(questId); + if (quest != null) + quest.LoadRewardChoiceItems(result.GetFields()); + else + Log.outError(LogFilter.Sql, $"Table `quest_reward_choice_items` has data for quest {questId} but such quest does not exist"); + } while (result.NextRow()); + } + + + // Load `quest_reward_display_spell` + // 0 1 2 + result = DB.World.Query("SELECT QuestID, SpellID, PlayerConditionID FROM quest_reward_display_spell ORDER BY QuestID ASC, Idx ASC"); + if (result.IsEmpty()) + { + Log.outError(LogFilter.ServerLoading, "Loaded 0 quest reward display spells. DB table `quest_reward_display_spell` is empty."); + } + else + { + do + { + uint questId = result.Read(0); + + var quest = _questTemplates.LookupByKey(questId); + if (quest != null) + quest.LoadRewardDisplaySpell(result.GetFields()); + else + Log.outError(LogFilter.Sql, $"Table `quest_reward_display_spell` has data for quest {questId} but such quest does not exist"); + } while (result.NextRow()); + } + + // Load `quest_details` // 0 1 2 3 4 5 6 7 8 result = DB.World.Query("SELECT ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4 FROM quest_details"); @@ -6639,7 +6684,7 @@ namespace Game } while (result.NextRow()); } - // Load `quest_objectives` + // Load `quest_mail_sender` // 0 1 result = DB.World.Query("SELECT QuestId, RewardMailSenderEntry FROM quest_mail_sender"); if (result.IsEmpty()) @@ -6785,11 +6830,8 @@ namespace Game } } - if (qinfo.MinLevel == -1 || qinfo.MinLevel > SharedConst.DefaultMaxLevel) - { - Log.outError(LogFilter.Sql, "Quest {0} should be disabled because `MinLevel` = {1}", qinfo.Id, qinfo.MinLevel); - // no changes needed, sending -1 in SMSGQUESTQUERYRESPONSE is valid - } + if (qinfo.ContentTuningId != 0 && !CliDB.ContentTuningStorage.ContainsKey(qinfo.ContentTuningId)) + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `ContentTuningID` = {qinfo.ContentTuningId} but content tuning with this id does not exist."); // client quest log visual (area case) if (qinfo.QuestSortID > 0) @@ -7073,24 +7115,34 @@ namespace Game var id = qinfo.RewardChoiceItemId[j]; if (id != 0) { - if (Global.ObjectMgr.GetItemTemplate(id) == null) + switch (qinfo.RewardChoiceItemType[j]) { - Log.outError(LogFilter.Sql, "Quest {0} has `RewardChoiceItemId{1}` = {2} but item with entry {2} does not exist, quest will not reward this item.", - qinfo.Id, j + 1, id); - qinfo.RewardChoiceItemId[j] = 0; // no changes, quest will not reward this + case LootItemType.Item: + if (Global.ObjectMgr.GetItemTemplate(id) == null) + { + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `RewardChoiceItemId{j + 1}` = {id} but item with entry {id} does not exist, quest will not reward this item."); + qinfo.RewardChoiceItemId[j] = 0; // no changes, quest will not reward this + } + break; + case LootItemType.Currency: + if (!CliDB.CurrencyTypesStorage.HasRecord(id)) + { + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `RewardChoiceItemId{j + 1}` = {id} but currency with id {id} does not exist, quest will not reward this currency."); + qinfo.RewardChoiceItemId[j] = 0; // no changes, quest will not reward this + } + break; + default: + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `RewardChoiceItemType{j + 1}` = {qinfo.RewardChoiceItemType[j]} but it is not a valid item type, reward removed."); + qinfo.RewardChoiceItemId[j] = 0; + break; } if (qinfo.RewardChoiceItemCount[j] == 0) - { - Log.outError(LogFilter.Sql, "Quest {0} has `RewardChoiceItemId{1}` = {2} but `RewardChoiceItemCount{1}` = 0, quest can't be done.", - qinfo.Id, j + 1, id); - // no changes, quest can't be done - } + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `RewardChoiceItemId{j + 1}` = {id} but `RewardChoiceItemCount{j + 1}` = 0, quest can't be done."); } else if (qinfo.RewardChoiceItemCount[j] > 0) { - Log.outError(LogFilter.Sql, "Quest {0} has `RewardChoiceItemId{1}` = 0 but `RewardChoiceItemCount{1}` = {3}.", - qinfo.Id, j + 1, qinfo.RewardChoiceItemCount[j]); + Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} has `RewardChoiceItemId{j + 1}` = 0 but `RewardChoiceItemCount{j + 1}` = {qinfo.RewardChoiceItemCount[j]}."); // no changes, quest ignore this data } } @@ -7146,26 +7198,6 @@ namespace Game } } - for (uint i = 0; i < SharedConst.QuestRewardDisplaySpellCount; ++i) - { - if (qinfo.RewardDisplaySpell[i] != 0) - { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(qinfo.RewardSpell, Difficulty.None); - if (spellInfo == null) - { - Log.outError(LogFilter.Sql, "Quest {0} has `RewardSpell` = {1} but spell {2} does not exist, spell removed as display reward.", - qinfo.Id, qinfo.RewardSpell, qinfo.RewardSpell); - qinfo.RewardDisplaySpell[i] = 0; // no spell reward will display for this quest - } - else if (!Global.SpellMgr.IsSpellValid(spellInfo)) - { - Log.outError(LogFilter.Sql, "Quest {0} has `RewardSpell` = {1} but spell {2} is broken, quest will not have a spell reward.", - qinfo.Id, qinfo.RewardSpell, qinfo.RewardSpell); - qinfo.RewardDisplaySpell[i] = 0; // no spell reward will display for this quest - } - } - } - if (qinfo.RewardSpell > 0) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(qinfo.RewardSpell, Difficulty.None); @@ -7485,33 +7517,33 @@ namespace Game uint count = 0; - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - SQLResult result = DB.World.Query("SELECT QuestID, BlobIndex, Idx1, ObjectiveIndex, QuestObjectiveID, QuestObjectID, MapID, UiMapID, Priority, Flags, WorldEffectID, PlayerConditionID, SpawnTrackingID, AlwaysAllowMergingBlobs FROM quest_poi order by QuestID, Idx1"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + SQLResult result = DB.World.Query("SELECT QuestID, BlobIndex, Idx1, ObjectiveIndex, QuestObjectiveID, QuestObjectID, MapID, UiMapID, Priority, Flags, WorldEffectID, PlayerConditionID, NavigationPlayerConditionID, SpawnTrackingID, AlwaysAllowMergingBlobs FROM quest_poi order by QuestID, Idx1"); if (result.IsEmpty()) { Log.outError(LogFilter.ServerLoading, "Loaded 0 quest POI definitions. DB table `quest_poi` is empty."); return; } - // 0 1 2 3 - SQLResult points = DB.World.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx1, Idx2"); - Dictionary> POIs = new Dictionary>(); + Dictionary> allPoints = new Dictionary>(); - if (!points.IsEmpty()) + // 0 1 2 3 4 + SQLResult pointsResult = DB.World.Query("SELECT QuestID, Idx1, X, Y, Z FROM quest_poi_points ORDER BY QuestID DESC, Idx1, Idx2"); + if (!pointsResult.IsEmpty()) { do { - uint questId = points.Read(0); - int Idx1 = points.Read(1); - int x = points.Read(2); - int y = points.Read(3); + uint questId = pointsResult.Read(0); + int Idx1 = pointsResult.Read(1); + int x = pointsResult.Read(2); + int y = pointsResult.Read(3); + int z = pointsResult.Read(4); - if (!POIs.ContainsKey(questId)) - POIs[questId] = new MultiMap(); + if (!allPoints.ContainsKey(questId)) + allPoints[questId] = new MultiMap(); - QuestPOIBlobPoint point = new QuestPOIBlobPoint(x, y); - POIs[questId].Add(Idx1, point); - } while (points.NextRow()); + allPoints[questId].Add(Idx1, new QuestPOIBlobPoint(x, y, z)); + } while (pointsResult.NextRow()); } do @@ -7528,25 +7560,32 @@ namespace Game int flags = result.Read(9); int worldEffectID = result.Read(10); int playerConditionID = result.Read(11); - int spawnTrackingID = result.Read(12); - bool alwaysAllowMergingBlobs = result.Read(13); + int navigationPlayerConditionID = result.Read(12); + int spawnTrackingID = result.Read(13); + bool alwaysAllowMergingBlobs = result.Read(14); if (Global.ObjectMgr.GetQuestTemplate(questID) == null) - Log.outError(LogFilter.Sql, "`quest_poi` quest id ({0}) Idx1 ({1}) does not exist in `quest_template`", questID, idx1); + Log.outError(LogFilter.Sql, $"`quest_poi` quest id ({questID}) Idx1 ({idx1}) does not exist in `quest_template`"); - if (!POIs.ContainsKey(questID) || !POIs[questID].ContainsKey(idx1)) + var blobs = allPoints.LookupByKey(questID); + if (blobs != null) { - Log.outError(LogFilter.Sql, "Table quest_poi references unknown quest points for quest {0} POI id {1}", questID, blobIndex); - continue; + var points = blobs.LookupByKey(idx1); + if (!points.Empty()) + { + if (!_questPOIStorage.ContainsKey(questID)) + _questPOIStorage[questID] = new QuestPOIData(questID); + + QuestPOIData poiData = _questPOIStorage[questID]; + poiData.QuestID = questID; + poiData.Blobs.Add(new QuestPOIBlobData(blobIndex, objectiveIndex, questObjectiveID, questObjectID, mapID, uiMapId, priority, flags, + worldEffectID, playerConditionID, navigationPlayerConditionID, spawnTrackingID, points, alwaysAllowMergingBlobs)); + ++count; + continue; + } } + Log.outError(LogFilter.Sql, $"Table quest_poi references unknown quest points for quest {questID} POI id {blobIndex}"); - if (!_questPOIStorage.ContainsKey(questID)) - _questPOIStorage[questID] = new QuestPOIData(questID); - - QuestPOIData poiData = _questPOIStorage[questID]; - poiData.Blobs.Add(new QuestPOIBlobData(blobIndex, objectiveIndex, questObjectiveID, questObjectID, mapID, uiMapId, priority, flags, worldEffectID, playerConditionID, spawnTrackingID, POIs[questID][idx1], alwaysAllowMergingBlobs)); - - ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest POI definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); @@ -9116,6 +9155,8 @@ namespace Game uint itemRewardCount = 0; uint currencyRewardCount = 0; uint factionRewardCount = 0; + uint itemChoiceRewardCount = 0; + uint mawPowersCount = 0; do { @@ -9131,7 +9172,11 @@ namespace Game } while (choiceResult.NextRow()); - SQLResult responses = DB.World.Query("SELECT ChoiceId, ResponseId, ChoiceArtFileId, Flags, WidgetSetID, UiTextureAtlasElementID, SoundKitID, GroupID, Answer, Header, SubHeader, ButtonTemplate, Description, Confirmation, RewardQuestID FROM playerchoice_response ORDER BY `Index` ASC"); + // 0 1 2 3 4 5 + SQLResult responses = DB.World.Query("SELECT ChoiceId, ResponseId, ResponseIdentifier, ChoiceArtFileId, Flags, WidgetSetID, " + + //6 7 8 9 10 11 12 13 14 15 16 + "UiTextureAtlasElementID, SoundKitID, GroupID, UiTextureKitID, Answer, Header, SubHeader, ButtonTemplate, Description, Confirmation, RewardQuestID " + + "FROM playerchoice_response ORDER BY `Index` ASC"); if (!responses.IsEmpty()) { do @@ -9149,20 +9194,23 @@ namespace Game PlayerChoiceResponse response = new PlayerChoiceResponse(); response.ResponseId = responseId; - response.ChoiceArtFileId = responses.Read(2); - response.Flags = responses.Read(3); - response.WidgetSetID = responses.Read(4); - response.UiTextureAtlasElementID = responses.Read(5); - response.SoundKitID = responses.Read(6); - response.GroupID = responses.Read(7); - response.Answer = responses.Read(8); - response.Header = responses.Read(9); - response.SubHeader = responses.Read(10); - response.ButtonTooltip = responses.Read(11); - response.Description = responses.Read(12); - response.Confirmation = responses.Read(13); - if (!responses.IsNull(14)) - response.RewardQuestID.Set(responses.Read(14)); + response.ResponseIdentifier = responses.Read(2); + response.ChoiceArtFileId = responses.Read(3); + response.Flags = responses.Read(4); + response.WidgetSetID = responses.Read(5); + response.UiTextureAtlasElementID = responses.Read(6); + response.SoundKitID = responses.Read(7); + response.GroupID = responses.Read(8); + response.UiTextureKitID = responses.Read(9); + response.Answer = responses.Read(10); + response.Header = responses.Read(11); + response.SubHeader = responses.Read(12); + response.ButtonTooltip = responses.Read(13); + response.Description = responses.Read(14); + response.Confirmation = responses.Read(15); + if (!responses.IsNull(16)) + response.RewardQuestID.Set(responses.Read(16)); + ++responseCount; choice.Responses[responseId] = response; @@ -9199,7 +9247,6 @@ namespace Game reward.HonorPointCount = rewards.Read(7); reward.Money = rewards.Read(8); reward.Xp = rewards.Read(9); - ++rewardCount; if (reward.TitleId != 0 && !CliDB.CharTitlesStorage.ContainsKey(reward.TitleId)) { @@ -9221,6 +9268,7 @@ namespace Game } choice.Responses[responseId].Reward.Set(reward); + ++rewardCount; } while (rewards.NextRow()); } @@ -9269,8 +9317,8 @@ namespace Game continue; } - itemRewardCount++; response.Reward.Value.Items.Add(new PlayerChoiceResponseRewardItem(itemId, bonusListIds, quantity)); + itemRewardCount++; } while (rewardItem.NextRow()); } @@ -9311,8 +9359,8 @@ namespace Game continue; } - currencyRewardCount++; response.Reward.Value.Currency.Add(new PlayerChoiceResponseRewardEntry(currencyId, quantity)); + currencyRewardCount++; } while (rewardCurrency.NextRow()); } @@ -9353,14 +9401,96 @@ namespace Game continue; } - factionRewardCount++; response.Reward.Value.Faction.Add(new PlayerChoiceResponseRewardEntry(factionId, quantity)); + factionRewardCount++; } while (rewardFaction.NextRow()); } - Log.outInfo(LogFilter.ServerLoading, $"Loaded {_playerChoices.Count} player choices, {responseCount} responses, {rewardCount} rewards, {itemRewardCount} item rewards, {currencyRewardCount} " + - $"currency rewards and {factionRewardCount} faction rewards in {Time.GetMSTimeDiffToNow(oldMSTime)} ms."); + SQLResult rewardItems = DB.World.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item_choice ORDER BY `Index` ASC"); + if (!rewardItems.IsEmpty()) + { + do + { + int choiceId = rewardItems.Read(0); + int responseId = rewardItems.Read(1); + uint itemId = rewardItems.Read(2); + StringArray bonusListIDsTok = new StringArray(rewardItems.Read(3), ' '); + List bonusListIds = new List(); + foreach (string token in bonusListIDsTok) + bonusListIds.Add(int.Parse(token)); + + int quantity = rewardItems.Read(4); + + PlayerChoice choice = _playerChoices.LookupByKey(choiceId); + if (choice == null) + { + Log.outError(LogFilter.Sql, $"Table `playerchoice_response_reward_item_choice` references non-existing ChoiceId: {choiceId} (ResponseId: {responseId}), skipped"); + continue; + } + + var response = choice.Responses.Find(playerChoiceResponse => { return playerChoiceResponse.ResponseId == responseId; }); + if (response == null) + { + Log.outError(LogFilter.Sql, $"Table `playerchoice_response_reward_item_choice` references non-existing ResponseId: {responseId} for ChoiceId {choiceId}, skipped"); + continue; + } + + if (!response.Reward.HasValue) + { + Log.outError(LogFilter.Sql, $"Table `playerchoice_response_reward_item_choice` references non-existing player choice reward for ChoiceId {choiceId}, ResponseId: {responseId}, skipped"); + continue; + } + + if (GetItemTemplate(itemId) == null) + { + Log.outError(LogFilter.Sql, $"Table `playerchoice_response_reward_item_choice` references non-existing item {itemId} for ChoiceId {choiceId}, ResponseId: {responseId}, skipped"); + continue; + } + + response.Reward.ItemChoices.Add(itemId, bonusListIds, quantity); + itemChoiceRewardCount++; + + } while (rewards.NextRow()); + } + + SQLResult mawPowersResult = DB.World.Query("SELECT ChoiceId, ResponseId, TypeArtFileID, Rarity, RarityColor, SpellID, MaxStacks FROM playerchoice_response_maw_power"); + if (!mawPowersResult.IsEmpty()) + { + do + { + int choiceId = mawPowersResult.Read(0); + int responseId = mawPowersResult.Read(1); + + PlayerChoice choice = _playerChoices.LookupByKey(choiceId); + if (choice == null) + { + Log.outError(LogFilter.Sql, $"Table `playerchoice_response_maw_power` references non-existing ChoiceId: {choiceId} (ResponseId: {responseId}), skipped"); + continue; + } + + var response = choice.Responses.Find(playerChoiceResponse => { return playerChoiceResponse.ResponseId == responseId; }); + if (response == null) + { + Log.outError(LogFilter.Sql, "Table `playerchoice_response_maw_power` references non-existing ResponseId: {responseId} for ChoiceId {choiceId}, skipped"); + continue; + } + + PlayerChoiceResponseMawPower mawPower = new PlayerChoiceResponseMawPower(); + mawPower.TypeArtFileID = mawPowersResult.Read(2); + mawPower.Rarity = mawPowersResult.Read(3); + mawPower.RarityColor = mawPowersResult.Read(4); + mawPower.SpellID = mawPowersResult.Read(5); + mawPower.MaxStacks = mawPowersResult.Read(6); + response.MawPower.Add(mawPower); + + ++mawPowersCount; + + } while (mawPowersResult.NextRow()); + } + + Log.outInfo(LogFilter.ServerLoading, $"Loaded {_playerChoices.Count} player choices, {responseCount} responses, {rewardCount} rewards, {itemRewardCount} item rewards, " + + $"{currencyRewardCount} currency rewards, {factionRewardCount} faction rewards, {itemChoiceRewardCount} item choice rewards and {mawPowersCount} maw powers in {Time.GetMSTimeDiffToNow(oldMSTime)} ms."); } public void LoadPlayerChoicesLocale() { @@ -10506,7 +10636,7 @@ namespace Game public bool AlwaysAllowMergingBlobs; public QuestPOIBlobData(int blobIndex, int objectiveIndex, int questObjectiveID, int questObjectID, int mapID, int uiMapID, int priority, int flags, - int worldEffectID, int playerConditionID, int spawnTrackingID, List points, bool alwaysAllowMergingBlobs) + int worldEffectID, int playerConditionID, int navigationPlayerConditionID, int spawnTrackingID, List points, bool alwaysAllowMergingBlobs) { BlobIndex = blobIndex; ObjectiveIndex = objectiveIndex; @@ -10518,6 +10648,7 @@ namespace Game Flags = flags; WorldEffectID = worldEffectID; PlayerConditionID = playerConditionID; + NavigationPlayerConditionID = navigationPlayerConditionID; SpawnTrackingID = spawnTrackingID; Points = points; AlwaysAllowMergingBlobs = alwaysAllowMergingBlobs; @@ -11000,17 +11131,29 @@ namespace Game public List Items = new List(); public List Currency = new List(); public List Faction = new List(); + public List ItemChoices = new List(); + } + + public struct PlayerChoiceResponseMawPower + { + public int TypeArtFileID; + public int Rarity; + public uint RarityColor; + public int SpellID; + public int MaxStacks; } public class PlayerChoiceResponse { public int ResponseId; + public ushort ResponseIdentifier; public int ChoiceArtFileId; public int Flags; public uint WidgetSetID; public uint UiTextureAtlasElementID; public uint SoundKitID; public byte GroupID; + public int UiTextureKitID; public string Answer; public string Header; public string SubHeader; @@ -11019,6 +11162,7 @@ namespace Game public string Confirmation; public Optional Reward; public Optional RewardQuestID; + public Optional MawPower; } public class PlayerChoice diff --git a/Source/Game/Handlers/LFGHandler.cs b/Source/Game/Handlers/LFGHandler.cs index 5eb85d0b6..8b898900d 100644 --- a/Source/Game/Handlers/LFGHandler.cs +++ b/Source/Game/Handlers/LFGHandler.cs @@ -354,7 +354,7 @@ namespace Game foreach (var it in joinData.lockmap) { var blackList = new LFGBlackListPkt(); - blackList.PlayerGuid = it.Key; + blackList.PlayerGuid.Set(it.Key); foreach (var lockInfo in it.Value) { diff --git a/Source/Game/Networking/Packets/MiscPackets.cs b/Source/Game/Networking/Packets/MiscPackets.cs index 2152871a1..9adca677a 100644 --- a/Source/Game/Networking/Packets/MiscPackets.cs +++ b/Source/Game/Networking/Packets/MiscPackets.cs @@ -357,7 +357,7 @@ namespace Game.Networking.Packets InstanceGroupSize = new Optional(); RestrictedAccountMaxLevel = new Optional(); - RestrictedAccountMaxMoney = new Optional(); + RestrictedAccountMaxMoney = new Optional(); } public override void Write() diff --git a/Source/Game/Networking/Packets/MovementPackets.cs b/Source/Game/Networking/Packets/MovementPackets.cs index b27d7e3df..70e666f6a 100644 --- a/Source/Game/Networking/Packets/MovementPackets.cs +++ b/Source/Game/Networking/Packets/MovementPackets.cs @@ -1261,7 +1261,7 @@ namespace Game.Networking.Packets } } - struct MonsterSplineAnimTierTransition + public struct MonsterSplineAnimTierTransition { public int TierTransitionID; public uint StartTime; @@ -1277,7 +1277,7 @@ namespace Game.Networking.Packets } } - class MonsterSplineUnknown901 + public class MonsterSplineUnknown901 { public Array Data = new Array(16); @@ -1303,13 +1303,13 @@ namespace Game.Networking.Packets public class TeleportLocation { - public Vector4 Pos; + public Position Pos; public int Unused901_1 = -1; public int Unused901_2 = -1; public void Write(WorldPacket data) { - data.WriteVector4(Pos); + data.WriteXYZO(Pos); data.WriteInt32(Unused901_1); data.WriteInt32(Unused901_2); } diff --git a/Source/Game/Networking/Packets/QuestPackets.cs b/Source/Game/Networking/Packets/QuestPackets.cs index c91039594..66fff92e1 100644 --- a/Source/Game/Networking/Packets/QuestPackets.cs +++ b/Source/Game/Networking/Packets/QuestPackets.cs @@ -1006,7 +1006,7 @@ namespace Game.Networking.Packets } public class QuestRewards - { + { public uint ChoiceItemCount; public uint ItemCount; public uint Money; @@ -1016,20 +1016,20 @@ namespace Game.Networking.Packets public uint Honor; public uint Title; public uint FactionFlags; - public Array SpellCompletionDisplayID = new Array(SharedConst.QuestRewardDisplaySpellCount); + public int[] SpellCompletionDisplayID = new int[SharedConst.QuestRewardDisplaySpellCount]; public uint SpellCompletionID; public uint SkillLineID; public uint NumSkillUps; public uint TreasurePickerID; - public Array ChoiceItems = new Array(SharedConst.QuestRewardChoicesCount); - public Array ItemID = new Array(SharedConst.QuestRewardItemCount); - public Array ItemQty = new Array(SharedConst.QuestRewardItemCount); - public Array FactionID = new Array(SharedConst.QuestRewardReputationsCount); - public Array FactionValue = new Array(SharedConst.QuestRewardReputationsCount); - public Array FactionOverride = new Array(SharedConst.QuestRewardReputationsCount); - public Array FactionCapIn = new Array(SharedConst.QuestRewardReputationsCount); - public Array CurrencyID = new Array(SharedConst.QuestRewardCurrencyCount); - public Array CurrencyQty = new Array(SharedConst.QuestRewardCurrencyCount); + public QuestChoiceItem[] ChoiceItems = new QuestChoiceItem[SharedConst.QuestRewardChoicesCount]; + public uint[] ItemID = new uint[SharedConst.QuestRewardItemCount]; + public uint[] ItemQty = new uint[SharedConst.QuestRewardItemCount]; + public uint[] FactionID = new uint[SharedConst.QuestRewardReputationsCount]; + public int[] FactionValue = new int[SharedConst.QuestRewardReputationsCount]; + public int[] FactionOverride = new int[SharedConst.QuestRewardReputationsCount]; + public int[] FactionCapIn = new int[SharedConst.QuestRewardReputationsCount]; + public uint[] CurrencyID = new uint[SharedConst.QuestRewardCurrencyCount]; + public uint[] CurrencyQty = new uint[SharedConst.QuestRewardCurrencyCount]; public bool IsBoostSpell; public void Write(WorldPacket data) diff --git a/Source/Game/Networking/Packets/SpellPackets.cs b/Source/Game/Networking/Packets/SpellPackets.cs index b10801808..5be12e475 100644 --- a/Source/Game/Networking/Packets/SpellPackets.cs +++ b/Source/Game/Networking/Packets/SpellPackets.cs @@ -394,14 +394,25 @@ namespace Game.Networking.Packets public ObjectGuid CastID; } - class CastFailed : ServerPacket + class CastFailedBase : ServerPacket { public ObjectGuid CastID; public int SpellID; - public SpellCastVisual Visual; public SpellCastResult Reason; public int FailedArg1 = -1; - public int FailedArg2 = -1; + public int FailedArg2 = -1; + + public CastFailedBase(ServerOpcodes opcode, ConnectionType connectionType) : base(opcode, connectionType) { } + + public override void Write() + { + throw new NotImplementedException(); + } + } + + class CastFailed : CastFailedBase + { + public SpellCastVisual Visual; public CastFailed() : base(ServerOpcodes.CastFailed, ConnectionType.Instance) { } @@ -418,14 +429,8 @@ namespace Game.Networking.Packets } } - class PetCastFailed : ServerPacket + class PetCastFailed : CastFailedBase { - public ObjectGuid CastID; - public int SpellID; - public SpellCastResult Reason; - public int FailedArg1 = -1; - public int FailedArg2 = -1; - public PetCastFailed() : base(ServerOpcodes.PetCastFailed, ConnectionType.Instance) { } public override void Write() diff --git a/Source/Game/Quest/Quest.cs b/Source/Game/Quest/Quest.cs index 0499dc3e6..56e80d419 100644 --- a/Source/Game/Quest/Quest.cs +++ b/Source/Game/Quest/Quest.cs @@ -32,43 +32,35 @@ namespace Game { Id = fields.Read(0); Type = (QuestType)fields.Read(1); - Level = fields.Read(2); - ScalingFactionGroup = fields.Read(3); - - MaxScalingLevel = fields.Read(4); - PackageID = fields.Read(5); - MinLevel = fields.Read(6); - QuestSortID = fields.Read(7); - QuestInfoID = fields.Read(8); - SuggestedPlayers = fields.Read(9); - NextQuestInChain = fields.Read(10); - RewardXPDifficulty = fields.Read(11); - RewardXPMultiplier = fields.Read(12); - RewardMoney = fields.Read(13); - RewardMoneyDifficulty = fields.Read(14); - RewardMoneyMultiplier = fields.Read(15); - RewardBonusMoney = fields.Read(16); - - for (int i = 0; i < SharedConst.QuestRewardDisplaySpellCount; ++i) - RewardDisplaySpell[i] = fields.Read(17 + i); - - RewardSpell = fields.Read(20); - RewardHonor = fields.Read(21); - RewardKillHonor = fields.Read(22); - SourceItemId = fields.Read(23); - RewardArtifactXPDifficulty = fields.Read(24); - RewardArtifactXPMultiplier = fields.Read(25); - RewardArtifactCategoryID = fields.Read(26); - Flags = (QuestFlags)fields.Read(27); - FlagsEx = (QuestFlagsEx)fields.Read(28); - FlagsEx2 = (QuestFlagsEx2)fields.Read(29); + PackageID = fields.Read(2); + ContentTuningId = fields.Read(3); + QuestSortID = fields.Read(4); + QuestInfoID = fields.Read(5); + SuggestedPlayers = fields.Read(6); + NextQuestInChain = fields.Read(7); + RewardXPDifficulty = fields.Read(8); + RewardXPMultiplier = fields.Read(9); + RewardMoney = fields.Read(10); + RewardMoneyDifficulty = fields.Read(11); + RewardMoneyMultiplier = fields.Read(12); + RewardBonusMoney = fields.Read(13); + RewardSpell = fields.Read(14); + RewardHonor = fields.Read(15); + RewardKillHonor = fields.Read(16); + SourceItemId = fields.Read(17); + RewardArtifactXPDifficulty = fields.Read(18); + RewardArtifactXPMultiplier = fields.Read(19); + RewardArtifactCategoryID = fields.Read(20); + Flags = (QuestFlags)fields.Read(21); + FlagsEx = (QuestFlagsEx)fields.Read(22); + FlagsEx2 = (QuestFlagsEx2)fields.Read(23); for (int i = 0; i < SharedConst.QuestItemDropCount; ++i) { - RewardItemId[i] = fields.Read(30 + i * 4); - RewardItemCount[i] = fields.Read(31 + i * 4); - ItemDrop[i] = fields.Read(32 + i * 4); - ItemDropQuantity[i] = fields.Read(33 + i * 4); + RewardItemId[i] = fields.Read(24 + i * 4); + RewardItemCount[i] = fields.Read(25 + i * 4); + ItemDrop[i] = fields.Read(26 + i * 4); + ItemDropQuantity[i] = fields.Read(27 + i * 4); if (RewardItemId[i] != 0) ++_rewItemsCount; @@ -76,66 +68,92 @@ namespace Game for (int i = 0; i < SharedConst.QuestRewardChoicesCount; ++i) { - RewardChoiceItemId[i] = fields.Read(46 + i * 3); - RewardChoiceItemCount[i] = fields.Read(47 + i * 3); - RewardChoiceItemDisplayId[i] = fields.Read(48 + i * 3); + RewardChoiceItemId[i] = fields.Read(40 + i * 3); + RewardChoiceItemCount[i] = fields.Read(41 + i * 3); + RewardChoiceItemDisplayId[i] = fields.Read(42 + i * 3); if (RewardChoiceItemId[i] != 0) ++_rewChoiceItemsCount; } - POIContinent = fields.Read(64); - POIx = fields.Read(65); - POIy = fields.Read(66); - POIPriority = fields.Read(67); + POIContinent = fields.Read(58); + POIx = fields.Read(59); + POIy = fields.Read(60); + POIPriority = fields.Read(61); - RewardTitleId = fields.Read(68); - RewardArenaPoints = fields.Read(69); - RewardSkillId = fields.Read(70); - RewardSkillPoints = fields.Read(71); + RewardTitleId = fields.Read(62); + RewardArenaPoints = fields.Read(63); + RewardSkillId = fields.Read(64); + RewardSkillPoints = fields.Read(65); - QuestGiverPortrait = fields.Read(72); - QuestGiverPortraitMount = fields.Read(73); - QuestTurnInPortrait = fields.Read(74); + QuestGiverPortrait = fields.Read(66); + QuestGiverPortraitMount = fields.Read(67); + QuestTurnInPortrait = fields.Read(68); for (int i = 0; i < SharedConst.QuestRewardReputationsCount; ++i) { - RewardFactionId[i] = fields.Read(75 + i * 4); - RewardFactionValue[i] = fields.Read(76 + i * 4); - RewardFactionOverride[i] = fields.Read(77 + i * 4); - RewardFactionCapIn[i] = fields.Read(78 + i * 4); + RewardFactionId[i] = fields.Read(69 + i * 4); + RewardFactionValue[i] = fields.Read(70 + i * 4); + RewardFactionOverride[i] = fields.Read(71 + i * 4); + RewardFactionCapIn[i] = fields.Read(72 + i * 4); } - RewardReputationMask = fields.Read(95); + RewardReputationMask = fields.Read(89); for (int i = 0; i < SharedConst.QuestRewardCurrencyCount; ++i) { - RewardCurrencyId[i] = fields.Read(96 + i * 2); - RewardCurrencyCount[i] = fields.Read(97 + i * 2); + RewardCurrencyId[i] = fields.Read(90 + i * 2); + RewardCurrencyCount[i] = fields.Read(91 + i * 2); if (RewardCurrencyId[i] != 0) ++_rewCurrencyCount; } - SoundAccept = fields.Read(104); - SoundTurnIn = fields.Read(105); - AreaGroupID = fields.Read(106); - LimitTime = fields.Read(107); - AllowableRaces = (long)fields.Read(108); - TreasurePickerID = fields.Read(109); - Expansion = fields.Read(110); - ManagedWorldStateID = fields.Read(111); - QuestSessionBonus = fields.Read(112); + SoundAccept = fields.Read(98); + SoundTurnIn = fields.Read(99); + AreaGroupID = fields.Read(100); + LimitTime = fields.Read(101); + AllowableRaces = (long)fields.Read(102); + TreasurePickerID = fields.Read(103); + Expansion = fields.Read(104); + ManagedWorldStateID = fields.Read(105); + QuestSessionBonus = fields.Read(106); - LogTitle = fields.Read(113); - LogDescription = fields.Read(114); - QuestDescription = fields.Read(115); - AreaDescription = fields.Read(116); - PortraitGiverText = fields.Read(117); - PortraitGiverName = fields.Read(118); - PortraitTurnInText = fields.Read(119); - PortraitTurnInName = fields.Read(120); - QuestCompletionLog = fields.Read(121); + LogTitle = fields.Read(107); + LogDescription = fields.Read(108); + QuestDescription = fields.Read(109); + AreaDescription = fields.Read(110); + PortraitGiverText = fields.Read(111); + PortraitGiverName = fields.Read(112); + PortraitTurnInText = fields.Read(113); + PortraitTurnInName = fields.Read(114); + QuestCompletionLog = fields.Read(115); + } + + public void LoadRewardDisplaySpell(SQLFields fields) + { + uint spellId = fields.Read(1); + uint playerConditionId = fields.Read(2); + + if (!Global.SpellMgr.HasSpellInfo(spellId, Difficulty.None)) + { + Log.outError(LogFilter.Sql, $"Table `quest_reward_display_spell` has non-existing Spell ({spellId}) set for quest {Id}. Skipped."); + return; + } + + if (playerConditionId != 0 && !CliDB.PlayerConditionStorage.ContainsKey(playerConditionId)) + { + Log.outError(LogFilter.Sql, $"Table `quest_reward_display_spell` has non-existing PlayerCondition ({spellId}) set for quest {Id}. Set to 0."); + playerConditionId = 0; + } + + RewardDisplaySpell.Add(new QuestRewardDisplaySpell(spellId, playerConditionId)); + } + + public void LoadRewardChoiceItems(SQLFields fields) + { + for (int i = 0; i < SharedConst.QuestRewardChoicesCount; ++i) + RewardChoiceItemType[i] = (LootItemType)fields.Read(1 + i); } public void LoadQuestDetails(SQLFields fields) @@ -310,9 +328,20 @@ namespace Game rewards.ArtifactCategoryID = RewardArtifactCategoryID; rewards.Title = RewardTitleId; rewards.FactionFlags = RewardReputationMask; - for (int i = 0; i < SharedConst.QuestRewardDisplaySpellCount; ++i) - rewards.SpellCompletionDisplayID[i] = (int)RewardDisplaySpell[i]; + var displaySpellItr = rewards.SpellCompletionDisplayID.begin(); + foreach (QuestRewardDisplaySpell displaySpell in RewardDisplaySpell) + { + PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(displaySpell.PlayerConditionId); + if (playerCondition != null) + if (!ConditionManager.IsPlayerMeetingCondition(player, playerCondition)) + continue; + + displaySpellItr = displaySpell.SpellId; + if (++displaySpellItr == null) + break; + } + rewards.SpellCompletionID = RewardSpell; rewards.SkillLineID = RewardSkillId; rewards.NumSkillUps = RewardSkillPoints; @@ -320,6 +349,7 @@ namespace Game for (int i = 0; i < SharedConst.QuestRewardChoicesCount; ++i) { + rewards.ChoiceItems[i].LootItemType = RewardChoiceItemType[i]; rewards.ChoiceItems[i].Item = new ItemInstance(); rewards.ChoiceItems[i].Item.ItemID = RewardChoiceItemId[i]; rewards.ChoiceItems[i].Quantity = RewardChoiceItemCount[i]; @@ -551,14 +581,20 @@ namespace Game public void SetEventIdForQuest(ushort eventId) { _eventIdForQuest = eventId; } public ushort GetEventIdForQuest() { return _eventIdForQuest; } + [Obsolete] + public int MinLevel; + [Obsolete] + public int Level; + [Obsolete] + public int ScalingFactionGroup; + [Obsolete] + public int MaxScalingLevel; + #region Fields public uint Id; public QuestType Type; - public int Level; - public int ScalingFactionGroup; - public int MaxScalingLevel; public uint PackageID; - public int MinLevel; + public uint ContentTuningId; public int QuestSortID; public uint QuestInfoID; public uint SuggestedPlayers; @@ -569,7 +605,7 @@ namespace Game public uint RewardMoneyDifficulty; public float RewardMoneyMultiplier; public uint RewardBonusMoney; - public uint[] RewardDisplaySpell = new uint[SharedConst.QuestRewardDisplaySpellCount]; + public List RewardDisplaySpell = new List(); public uint RewardSpell { get; set; } public uint RewardHonor; public uint RewardKillHonor; @@ -584,6 +620,7 @@ namespace Game public uint[] RewardItemCount = new uint[SharedConst.QuestRewardItemCount]; public uint[] ItemDrop = new uint[SharedConst.QuestItemDropCount]; public uint[] ItemDropQuantity = new uint[SharedConst.QuestItemDropCount]; + public LootItemType[] RewardChoiceItemType = new LootItemType[SharedConst.QuestRewardChoicesCount]; public uint[] RewardChoiceItemId = new uint[SharedConst.QuestRewardChoicesCount]; public uint[] RewardChoiceItemCount = new uint[SharedConst.QuestRewardChoicesCount]; public uint[] RewardChoiceItemDisplayId = new uint[SharedConst.QuestRewardChoicesCount]; @@ -662,7 +699,7 @@ namespace Game public uint ScriptId; public List DependentPreviousQuests = new List(); - public QueryQuestInfoResponse QueryData; + public QueryQuestInfoResponse[] QueryData = new QueryQuestInfoResponse[(int)Locale.Total]; uint _rewChoiceItemsCount; uint _rewItemsCount; @@ -729,6 +766,18 @@ namespace Game public StringArray RewardText = new StringArray((int)Locale.Total); } + public struct QuestRewardDisplaySpell + { + public uint SpellId; + public uint PlayerConditionId; + + public QuestRewardDisplaySpell(uint spellId, uint playerConditionId) + { + SpellId = spellId; + PlayerConditionId = playerConditionId; + } + } + public class QuestObjective { public uint Id; diff --git a/Source/Game/Scenarios/Scenario.cs b/Source/Game/Scenarios/Scenario.cs index 4ebb01ad7..f541fd14f 100644 --- a/Source/Game/Scenarios/Scenario.cs +++ b/Source/Game/Scenarios/Scenario.cs @@ -71,7 +71,7 @@ namespace Game.Scenarios { Player player = Global.ObjAccessor.FindPlayer(guid); if (player) - player.RewardQuest(quest, 0, null, false); + player.RewardQuest(quest, LootItemType.Item, 0, null, false); } } diff --git a/Source/Game/Scenarios/ScenarioManager.cs b/Source/Game/Scenarios/ScenarioManager.cs index a1cc46a88..075ff1505 100644 --- a/Source/Game/Scenarios/ScenarioManager.cs +++ b/Source/Game/Scenarios/ScenarioManager.cs @@ -151,45 +151,40 @@ namespace Game.Scenarios uint count = 0; - // 0 1 2 3 4 5 6 7 8 - SQLResult result = DB.World.Query("SELECT CriteriaTreeID, BlobIndex, Idx1, MapID, UiMapID, Priority, Flags, WorldEffectID, PlayerConditionID FROM scenario_poi ORDER BY CriteriaTreeID, Idx1"); + // 0 1 2 3 4 5 6 7 8 9 + SQLResult result = DB.World.Query("SELECT CriteriaTreeID, BlobIndex, Idx1, MapID, UiMapID, Priority, Flags, WorldEffectID, PlayerConditionID, NavigationPlayerConditionID FROM scenario_poi ORDER BY CriteriaTreeID, Idx1"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 scenario POI definitions. DB table `scenario_poi` is empty."); return; } - // 0 1 2 3 - SQLResult points = DB.World.Query("SELECT CriteriaTreeID, Idx1, X, Y FROM scenario_poi_points ORDER BY CriteriaTreeID DESC, Idx1, Idx2"); - List[][] POIs = new List[0][]; + Dictionary> allPoints = new Dictionary>(); - if (!points.IsEmpty()) + // 0 1 2 3 4 + SQLResult pointsResult = DB.World.Query("SELECT CriteriaTreeID, Idx1, X, Y, Z FROM scenario_poi_points ORDER BY CriteriaTreeID DESC, Idx1, Idx2"); + if (!pointsResult.IsEmpty()) { - // The first result should have the highest criteriaTreeId - uint criteriaTreeIdMax = points.Read(0); - POIs = new List[criteriaTreeIdMax + 1][]; - do { - int CriteriaTreeID = points.Read(0); - int Idx1 = points.Read(1); - int X = points.Read(2); - int Y = points.Read(3); + uint CriteriaTreeID = pointsResult.Read(0); + int Idx1 = pointsResult.Read(1); + int X = pointsResult.Read(2); + int Y = pointsResult.Read(3); + int Z = pointsResult.Read(4); - if (POIs[CriteriaTreeID] == null) - POIs[CriteriaTreeID] = new List[Idx1 + 10]; + if (!allPoints.ContainsKey(CriteriaTreeID)) + allPoints[CriteriaTreeID] = new MultiMap(); - if (POIs[CriteriaTreeID][Idx1] == null) - POIs[CriteriaTreeID][Idx1] = new List(); + allPoints[CriteriaTreeID].Add(Idx1, new ScenarioPOIPoint(X, Y, Z)); - POIs[CriteriaTreeID][Idx1].Add(new Vector2(X, Y)); - } while (points.NextRow()); + } while (pointsResult.NextRow()); } do { - int criteriaTreeID = result.Read(0); + uint criteriaTreeID = result.Read(0); int blobIndex = result.Read(1); int idx1 = result.Read(2); int mapID = result.Read(3); @@ -198,19 +193,28 @@ namespace Game.Scenarios int flags = result.Read(6); int worldEffectID = result.Read(7); int playerConditionID = result.Read(8); + int navigationPlayerConditionID = result.Read(9); - if (Global.CriteriaMgr.GetCriteriaTree((uint)criteriaTreeID) == null) - Log.outError(LogFilter.Sql, "`scenario_poi` CriteriaTreeID ({0}) Idx1 ({1}) does not correspond to a valid criteria tree", criteriaTreeID, idx1); + if (Global.CriteriaMgr.GetCriteriaTree(criteriaTreeID) == null) + Log.outError(LogFilter.Sql, $"`scenario_poi` CriteriaTreeID ({criteriaTreeID}) Idx1 ({idx1}) does not correspond to a valid criteria tree"); - if (criteriaTreeID < POIs.Length && idx1 < POIs[criteriaTreeID].Length) - _scenarioPOIStore.Add((uint)criteriaTreeID, new ScenarioPOI(blobIndex, mapID, uiMapID, priority, flags, worldEffectID, playerConditionID, POIs[criteriaTreeID][idx1])); - else - Log.outError(LogFilter.ServerLoading, "Table scenario_poi references unknown scenario poi points for criteria tree id {0} POI id {1}", criteriaTreeID, blobIndex); + var blobs = allPoints.LookupByKey(criteriaTreeID); + if (blobs != null) + { + var points = blobs.LookupByKey(idx1); + if (!points.Empty()) + { + _scenarioPOIStore.Add(criteriaTreeID, new ScenarioPOI(blobIndex, mapID, uiMapID, priority, flags, worldEffectID, playerConditionID, navigationPlayerConditionID, points)); + ++count; + continue; + } + } + + Log.outError(LogFilter.Sql, $"Table scenario_poi references unknown scenario poi points for criteria tree id {criteriaTreeID} POI id {blobIndex}"); - ++count; } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} scenario POI definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); + Log.outInfo(LogFilter.ServerLoading, "Loaded {count} scenario POI definitions in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public List GetScenarioPOIs(uint CriteriaTreeID) @@ -240,6 +244,20 @@ namespace Game.Scenarios public uint Scenario_H; } + public struct ScenarioPOIPoint + { + public int X; + public int Y; + public int Z; + + public ScenarioPOIPoint(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + } + public class ScenarioPOI { public int BlobIndex; @@ -250,9 +268,9 @@ namespace Game.Scenarios public int WorldEffectID; public int PlayerConditionID; public int NavigationPlayerConditionID; - public List Points = new List(); + public List Points = new List(); - public ScenarioPOI(int blobIndex, int mapID, int uiMapID, int priority, int flags, int worldEffectID, int playerConditionID, List points) + public ScenarioPOI(int blobIndex, int mapID, int uiMapID, int priority, int flags, int worldEffectID, int playerConditionID, int navigationPlayerConditionID, List points) { BlobIndex = blobIndex; MapID = mapID; @@ -261,6 +279,7 @@ namespace Game.Scenarios Flags = flags; WorldEffectID = worldEffectID; PlayerConditionID = playerConditionID; + NavigationPlayerConditionID = navigationPlayerConditionID; Points = points; } } diff --git a/Source/Game/Server/WorldConfig.cs b/Source/Game/Server/WorldConfig.cs index 971e9e239..67e088343 100644 --- a/Source/Game/Server/WorldConfig.cs +++ b/Source/Game/Server/WorldConfig.cs @@ -896,7 +896,6 @@ namespace Game // misc Values[WorldCfg.PdumpNoPaths] = GetDefaultValue("PlayerDump.DisallowPaths", true); Values[WorldCfg.PdumpNoOverwrite] = GetDefaultValue("PlayerDump.DisallowOverwrite", true); - Values[WorldCfg.UiQuestLevelsInDialogs] = GetDefaultValue("UI.ShowQuestLevelsInDialogs", false); // Wintergrasp battlefield Values[WorldCfg.WintergraspEnable] = GetDefaultValue("Wintergrasp.Enable", false); diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index f3e62bd9f..56fc5ec65 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -213,7 +213,7 @@ namespace Game.Spells AuraDataInfo auraData = auraInfo.AuraData.Value; auraData.CastID = aura.GetCastGUID(); auraData.SpellID = (int)aura.GetId(); - auraData.Visual.SpellXSpellVisualID = (int)aura.GetSpellXSpellVisualId(); + auraData.Visual.SpellXSpellVisualID = aura.GetSpellXSpellVisualId(); auraData.Flags = GetFlags(); if (aura.GetMaxDuration() > 0 && !aura.GetSpellInfo().HasAttribute(SpellAttr5.HideDuration)) auraData.Flags |= AuraFlags.Duration; diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 7855b9fe8..be1e77721 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -3525,7 +3525,7 @@ namespace Game.Spells result = SpellCastResult.DontReport; CastFailed castFailed = new CastFailed(); - castFailed.Visual.SpellXSpellVisualID = (int)m_SpellVisual; + castFailed.Visual.SpellXSpellVisualID = m_SpellVisual; FillSpellCastFailedArgs(castFailed, m_castId, m_spellInfo, result, m_customError, param1, param2, m_caster.ToPlayer()); m_caster.ToPlayer().SendPacket(castFailed); } @@ -3553,7 +3553,7 @@ namespace Game.Spells return; CastFailed packet = new CastFailed(); - packet.Visual.SpellXSpellVisualID = (int)spellVisual; + packet.Visual.SpellXSpellVisualID = spellVisual; FillSpellCastFailedArgs(packet, cast_count, spellInfo, result, customError, param1, param2, caster); caster.SendPacket(packet); } diff --git a/Source/WorldServer/WorldServer.conf.dist b/Source/WorldServer/WorldServer.conf.dist index 2dd1ad803..66c0fa892 100644 --- a/Source/WorldServer/WorldServer.conf.dist +++ b/Source/WorldServer/WorldServer.conf.dist @@ -3069,14 +3069,6 @@ PlayerDump.DisallowPaths = 1 PlayerDump.DisallowOverwrite = 1 -# -# UI.ShowQuestLevelsInDialogs -# Description: Show quest levels next to quest titles in UI dialogs -# Example: [13] Westfall Stew -# Default: 0 - (Do not show) - -UI.ShowQuestLevelsInDialogs = 0 - # # Calculate.Creature.Zone.Area.Data # Description: Calculate at loading creature zoneId / areaId and save in creature table (WARNING: SLOW WORLD SERVER STARTUP)