From e4240fcd94747099795bebced684d30c2cf81432 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 24 Aug 2021 12:23:39 -0400 Subject: [PATCH] Core/Creatures: Reworked setting move types in database Port From (https://github.com/TrinityCore/TrinityCore/commit/f8c03a90661610e289029c78d2055d7b73a5ad98) --- Source/Framework/Constants/CreatureConst.cs | 18 + Source/Framework/Constants/Language.cs | 2 +- .../Database/Databases/WorldDatabase.cs | 2 +- Source/Framework/Database/SQLResult.cs | 5 + Source/Game/Chat/Commands/NPCCommands.cs | 2 +- Source/Game/Chat/Commands/ReloadCommand.cs | 14 +- Source/Game/Entities/Creature/Creature.cs | 47 +- Source/Game/Entities/Creature/CreatureData.cs | 30 +- Source/Game/Globals/ObjectManager.cs | 994 ++++++++++-------- Source/Game/Server/WorldManager.cs | 3 + sql/base/auth_database.sql | 3 + .../auth/master/2018_03_08_00_auth.sql | 7 + ...021_08_24_00_world_2018_03_08_11_world.sql | 34 + 13 files changed, 669 insertions(+), 492 deletions(-) create mode 100644 sql/updates/auth/master/2018_03_08_00_auth.sql create mode 100644 sql/updates/world/master/2021_08_24_00_world_2018_03_08_11_world.sql diff --git a/Source/Framework/Constants/CreatureConst.cs b/Source/Framework/Constants/CreatureConst.cs index a580bbeed..0618ad623 100644 --- a/Source/Framework/Constants/CreatureConst.cs +++ b/Source/Framework/Constants/CreatureConst.cs @@ -375,4 +375,22 @@ namespace Framework.Constants MembersAssistMember = (MembersAssistLeader | LeaderAssistsMember), // every member will assist if any member is attacked IdleInFormation = 0x200, // The member will follow the leader when pathing idly } + + public enum CreatureGroundMovementType + { + None, + Run, + Hover, + + Max + } + + public enum CreatureFlightMovementType + { + None, + DisableGravity, + CanFly, + + Max + } } diff --git a/Source/Framework/Constants/Language.cs b/Source/Framework/Constants/Language.cs index a80a2e6f2..7b18e4c23 100644 --- a/Source/Framework/Constants/Language.cs +++ b/Source/Framework/Constants/Language.cs @@ -1173,7 +1173,7 @@ namespace Framework.Constants BanAccountYoubannedmessageWorld = 11006, BanAccountYoupermbannedmessageWorld = 11007, - NpcinfoInhabitType = 11008, + NpcinfoMovementData = 11008, NpcinfoFlagsExtra = 11009, InstanceLoginGamemasterException = 11010, diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index 16d68e159..ae9acaa63 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -76,7 +76,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, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_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, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId 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/Framework/Database/SQLResult.cs b/Source/Framework/Database/SQLResult.cs index d6af16e10..8e4e7ce15 100644 --- a/Source/Framework/Database/SQLResult.cs +++ b/Source/Framework/Database/SQLResult.cs @@ -121,5 +121,10 @@ namespace Framework.Database return values; } + + public bool IsNull(int column) + { + return _currentRow[column] == DBNull.Value; + } } } diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index 5d0a0f45d..35c4c11d1 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -148,7 +148,7 @@ namespace Game.Chat handler.SendSysMessage(CypherStrings.NpcinfoLevel, target.GetLevel()); handler.SendSysMessage(CypherStrings.NpcinfoEquipment, target.GetCurrentEquipmentId(), target.GetOriginalEquipmentId()); handler.SendSysMessage(CypherStrings.NpcinfoHealth, target.GetCreateHealth(), target.GetMaxHealth(), target.GetHealth()); - handler.SendSysMessage(CypherStrings.NpcinfoInhabitType, cInfo.InhabitType); + handler.SendSysMessage(CypherStrings.NpcinfoMovementData, target.GetMovementTemplate().ToString()); handler.SendSysMessage(CypherStrings.NpcinfoUnitFieldFlags, (uint)target.m_unitData.Flags); foreach (UnitFlags value in Enum.GetValues(typeof(UnitFlags))) diff --git a/Source/Game/Chat/Commands/ReloadCommand.cs b/Source/Game/Chat/Commands/ReloadCommand.cs index f36a96e9c..74325cc48 100644 --- a/Source/Game/Chat/Commands/ReloadCommand.cs +++ b/Source/Game/Chat/Commands/ReloadCommand.cs @@ -169,6 +169,15 @@ namespace Game.Chat return true; } + [Command("creature_movement_override", RBACPermissions.CommandReloadCreatureMovementOverride, true)] + static bool HandleReloadCreatureMovementOverrideCommand(StringArguments args, CommandHandler handler) + { + Log.outInfo(LogFilter.Server, "Re-Loading Creature movement overrides..."); + Global.ObjectMgr.LoadCreatureMovementOverrides(); + handler.SendGlobalGMSysMessage("DB table `creature_movement_override` reloaded."); + return true; + } + [Command("creature_onkill_reputation", RBACPermissions.CommandReloadCreatureOnkillReputation, true)] static bool HandleReloadOnKillReputationCommand(StringArguments args, CommandHandler handler) { @@ -902,7 +911,7 @@ namespace Game.Chat handler.SendGlobalGMSysMessage("Vehicle templates reloaded."); return true; } - + [Command("vehicle_template_accessory", RBACPermissions.CommandReloadVehicleTemplateAccessory, true)] static bool HandleReloadVehicleTemplateAccessoryCommand(StringArguments args, CommandHandler handler) { @@ -985,6 +994,7 @@ namespace Game.Chat HandleReloadCypherStringCommand(args, handler); HandleReloadGameTeleCommand(args, handler); + HandleReloadCreatureMovementOverrideCommand(args, handler); HandleReloadCreatureSummonGroupsCommand(args, handler); HandleReloadVehicleAccessoryCommand(args, handler); @@ -1119,4 +1129,4 @@ namespace Game.Chat } } } -} +} \ No newline at end of file diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 5a7900e1f..d74ec70f3 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -416,7 +416,7 @@ namespace Game.Entities ApplySpellImmune(0, SpellImmunity.Effect, SpellEffectName.AttackMe, true); } - if (cInfo.InhabitType.HasAnyFlag(InhabitType.Root)) + if (GetMovementTemplate().IsRooted()) SetControlled(true, UnitState.Root); UpdateMovementFlags(); @@ -2311,7 +2311,7 @@ namespace Game.Entities dist += GetCombatReach() + victim.GetCombatReach(); // to prevent creatures in air ignore attacks because distance is already too high... - if (GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Air)) + if (GetMovementTemplate().IsFlightAllowed()) return victim.IsInDist2d(m_homePosition, dist); else return victim.IsInDist(m_homePosition, dist); @@ -2356,7 +2356,7 @@ namespace Game.Entities //! Check using InhabitType as movement flags are assigned dynamically //! basing on whether the creature is in air or not //! Set MovementFlag_Hover. Otherwise do nothing. - if (Convert.ToBoolean(m_unitData.AnimTier & (byte)UnitBytes1Flags.Hover) && !Convert.ToBoolean(GetCreatureTemplate().InhabitType & InhabitType.Air)) + if (CanHover()) AddUnitMovementFlag(MovementFlag.Hover); } @@ -2463,6 +2463,15 @@ namespace Game.Entities bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.spawnPoint.GetMapId() != GetMapId(); } + public CreatureMovementData GetMovementTemplate() + { + CreatureMovementData movementOverride = Global.ObjectMgr.GetCreatureMovementOverride(m_spawnId); + if (movementOverride != null) + return movementOverride; + + return GetCreatureTemplate().Movement; + } + public void AllLootRemovedFromCorpse() { if (loot.loot_type != LootType.Skinning && !IsPet() && GetCreatureTemplate().SkinLootId != 0 && HasLootRecipient()) @@ -2822,25 +2831,31 @@ namespace Game.Entities // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) float ground = GetFloorZ(); - bool isInAir = (MathFunctions.fuzzyGt(GetPositionZ(), ground + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZ(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling + bool canHover = CanHover(); + bool isInAir = (MathFunctions.fuzzyGt(GetPositionZ(), ground + (canHover ? m_unitData.HoverHeight : 0.0f) + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZ(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling - if (GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Air) && isInAir && !IsFalling()) + if (GetMovementTemplate().IsFlightAllowed() && isInAir && !IsFalling()) { - if (GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Ground)) + if (GetMovementTemplate().Flight == CreatureFlightMovementType.CanFly) SetCanFly(true); else SetDisableGravity(true); + + if (!HasAuraType(AuraType.Hover)) + SetHover(false); } else { SetCanFly(false); SetDisableGravity(false); + if (CanHover() || HasAuraType(AuraType.Hover)) + SetHover(true); } if (!isInAir) SetFall(false); - SetSwim(GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Water) && IsInWater()); + SetSwim(GetMovementTemplate().IsSwimAllowed() && IsInWater()); } public override void SetObjectScale(float scale) @@ -3034,18 +3049,12 @@ namespace Game.Entities { return GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Guard); } - public bool CanWalk() - { - return GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Ground); - } - public override bool CanSwim() - { - return GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Water); - } - public override bool CanFly() - { - return GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Air); - } + + public bool CanWalk() { return GetMovementTemplate().IsGroundAllowed(); } + public override bool CanSwim() { return GetMovementTemplate().IsSwimAllowed() || IsPet();} + public override bool CanFly() { return GetMovementTemplate().IsFlightAllowed(); } + bool CanHover() { return GetMovementTemplate().Ground == CreatureGroundMovementType.Hover; } + public bool IsDungeonBoss() { return (GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.DungeonBoss)); } public override bool IsAffectedByDiminishingReturns() { return base.IsAffectedByDiminishingReturns() || GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.AllDiminish); } diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index 0e2976e37..fa3ae06cb 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -74,7 +74,7 @@ namespace Game.Entities public uint MaxGold; public string AIName; public uint MovementType; - public InhabitType InhabitType; + public CreatureMovementData Movement; public float HoverHeight; public float ModHealth; public float ModHealthExtra; @@ -347,6 +347,34 @@ namespace Game.Entities public CreatureData() : base(SpawnObjectType.Creature) { } } + public class CreatureMovementData + { + public CreatureGroundMovementType Ground; + public CreatureFlightMovementType Flight; + public bool Swim; + public bool Rooted; + + public CreatureMovementData() + { + Ground = CreatureGroundMovementType.Run; + Flight = CreatureFlightMovementType.None; + Swim = true; + Rooted = false; + } + + + + bool IsGroundAllowed() { return Ground != CreatureGroundMovementType.None; } + bool IsSwimAllowed() { return Swim; } + bool IsFlightAllowed() { return Flight != CreatureFlightMovementType.None; } + bool IsRooted() { return Rooted; } + + public override string ToString() + { + return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}"; + } + } + public class CreatureModelInfo { public float BoundingRadius; diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 9ff42c531..2d92e6327 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -42,8 +42,8 @@ namespace Game { for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i) { - _difficultyEntries[i] = new List(); - _hasDifficultyEntries[i] = new List(); + difficultyEntries[i] = new List(); + hasDifficultyEntries[i] = new List(); } } @@ -594,7 +594,7 @@ namespace Game uint oldMSTime = Time.GetMSTime(); gossipMenuItemsStorage.Clear(); - + // 0 1 2 3 4 5 6 SQLResult result = DB.World.Query("SELECT o.MenuId, o.OptionIndex, o.OptionIcon, o.OptionText, o.OptionBroadcastTextId, o.OptionType, o.OptionNpcFlag, " + // 7 8 9 10 11 12 @@ -1045,7 +1045,7 @@ namespace Game SQLResult result = DB.World.Query( "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' " + "UNION SELECT DISTINCT(ScriptName) FROM conversation_template WHERE ScriptName <> '' " + - "UNION SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' " + + "UNION SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' " + "UNION SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' " + "UNION SELECT DISTINCT(ScriptName) FROM criteria_data WHERE ScriptName <> '' AND type = 11 " + "UNION SELECT DISTINCT(ScriptName) FROM gameobject WHERE ScriptName <> '' " + @@ -1162,261 +1162,261 @@ namespace Game switch (tmp.command) { case ScriptCommands.Talk: + { + if (tmp.Talk.ChatType > ChatMsg.RaidBossWhisper) { - if (tmp.Talk.ChatType > ChatMsg.RaidBossWhisper) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid talk type (datalong = {1}) in SCRIPT_COMMAND_TALK for script id {2}", - tableName, tmp.Talk.ChatType, tmp.id); - continue; - } - if (!CliDB.BroadcastTextStorage.ContainsKey((uint)tmp.Talk.TextID)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid talk text id (dataint = {1}) in SCRIPT_COMMAND_TALK for script id {2}", - tableName, tmp.Talk.TextID, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid talk type (datalong = {1}) in SCRIPT_COMMAND_TALK for script id {2}", + tableName, tmp.Talk.ChatType, tmp.id); + continue; } + if (!CliDB.BroadcastTextStorage.ContainsKey((uint)tmp.Talk.TextID)) + { + Log.outError(LogFilter.Sql, "Table `{0}` has invalid talk text id (dataint = {1}) in SCRIPT_COMMAND_TALK for script id {2}", + tableName, tmp.Talk.TextID, tmp.id); + continue; + } + break; + } case ScriptCommands.Emote: + { + if (!CliDB.EmotesStorage.ContainsKey(tmp.Emote.EmoteID)) { - if (!CliDB.EmotesStorage.ContainsKey(tmp.Emote.EmoteID)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid emote id (datalong = {1}) in SCRIPT_COMMAND_EMOTE for script id {2}", - tableName, tmp.Emote.EmoteID, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid emote id (datalong = {1}) in SCRIPT_COMMAND_EMOTE for script id {2}", + tableName, tmp.Emote.EmoteID, tmp.id); + continue; } + break; + } case ScriptCommands.TeleportTo: + { + if (!CliDB.MapStorage.ContainsKey(tmp.TeleportTo.MapID)) { - if (!CliDB.MapStorage.ContainsKey(tmp.TeleportTo.MapID)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid map (Id: {1}) in SCRIPT_COMMAND_TELEPORT_TO for script id {2}", - tableName, tmp.TeleportTo.MapID, tmp.id); - continue; - } - - if (!GridDefines.IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid coordinates (X: {1} Y: {2} Z: {3} O: {4}) in SCRIPT_COMMAND_TELEPORT_TO for script id {5}", - tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid map (Id: {1}) in SCRIPT_COMMAND_TELEPORT_TO for script id {2}", + tableName, tmp.TeleportTo.MapID, tmp.id); + continue; } + if (!GridDefines.IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation)) + { + Log.outError(LogFilter.Sql, "Table `{0}` has invalid coordinates (X: {1} Y: {2} Z: {3} O: {4}) in SCRIPT_COMMAND_TELEPORT_TO for script id {5}", + tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id); + continue; + } + break; + } + case ScriptCommands.QuestExplored: + { + Quest quest = GetQuestTemplate(tmp.QuestExplored.QuestID); + if (quest == null) { - Quest quest = GetQuestTemplate(tmp.QuestExplored.QuestID); - if (quest == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}", - tableName, tmp.QuestExplored.QuestID, tmp.id); - continue; - } - - if (!quest.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", - tableName, tmp.QuestExplored.QuestID, tmp.id); - - // this will prevent quest completing without objective - quest.SetSpecialFlag(QuestSpecialFlags.ExplorationOrEvent); - - // continue; - quest objective requirement set and command can be allowed - } - - if (tmp.QuestExplored.Distance > SharedConst.DefaultVisibilityDistance) - { - Log.outError(LogFilter.Sql, "Table `{0}` has too large distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}", - tableName, tmp.QuestExplored.Distance, tmp.id); - continue; - } - - if (tmp.QuestExplored.Distance != 0 && tmp.QuestExplored.Distance > SharedConst.DefaultVisibilityDistance) - { - Log.outError(LogFilter.Sql, "Table `{0}` has too large distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, max distance is {3} or 0 for disable distance check", - tableName, tmp.QuestExplored.Distance, tmp.id, SharedConst.DefaultVisibilityDistance); - continue; - } - - if (tmp.QuestExplored.Distance != 0 && tmp.QuestExplored.Distance < SharedConst.InteractionDistance) - { - Log.outError(LogFilter.Sql, "Table `{0}` has too small distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, min distance is {3} or 0 for disable distance check", - tableName, tmp.QuestExplored.Distance, tmp.id, SharedConst.InteractionDistance); - continue; - } - - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}", + tableName, tmp.QuestExplored.QuestID, tmp.id); + continue; } + if (!quest.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent)) + { + Log.outError(LogFilter.Sql, "Table `{0}` has quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", + tableName, tmp.QuestExplored.QuestID, tmp.id); + + // this will prevent quest completing without objective + quest.SetSpecialFlag(QuestSpecialFlags.ExplorationOrEvent); + + // continue; - quest objective requirement set and command can be allowed + } + + if (tmp.QuestExplored.Distance > SharedConst.DefaultVisibilityDistance) + { + Log.outError(LogFilter.Sql, "Table `{0}` has too large distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}", + tableName, tmp.QuestExplored.Distance, tmp.id); + continue; + } + + if (tmp.QuestExplored.Distance != 0 && tmp.QuestExplored.Distance > SharedConst.DefaultVisibilityDistance) + { + Log.outError(LogFilter.Sql, "Table `{0}` has too large distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, max distance is {3} or 0 for disable distance check", + tableName, tmp.QuestExplored.Distance, tmp.id, SharedConst.DefaultVisibilityDistance); + continue; + } + + if (tmp.QuestExplored.Distance != 0 && tmp.QuestExplored.Distance < SharedConst.InteractionDistance) + { + Log.outError(LogFilter.Sql, "Table `{0}` has too small distance ({1}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, min distance is {3} or 0 for disable distance check", + tableName, tmp.QuestExplored.Distance, tmp.id, SharedConst.InteractionDistance); + continue; + } + + break; + } + case ScriptCommands.KillCredit: + { + if (GetCreatureTemplate(tmp.KillCredit.CreatureEntry) == null) { - if (GetCreatureTemplate(tmp.KillCredit.CreatureEntry) == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid creature (Entry: {1}) in SCRIPT_COMMAND_KILL_CREDIT for script id {2}", - tableName, tmp.KillCredit.CreatureEntry, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid creature (Entry: {1}) in SCRIPT_COMMAND_KILL_CREDIT for script id {2}", + tableName, tmp.KillCredit.CreatureEntry, tmp.id); + continue; } + break; + } case ScriptCommands.RespawnGameobject: + { + GameObjectData data = GetGameObjectData(tmp.RespawnGameObject.GOGuid); + if (data == null) { - GameObjectData data = GetGameObjectData(tmp.RespawnGameObject.GOGuid); - if (data == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {2}", - tableName, tmp.RespawnGameObject.GOGuid, tmp.id); - continue; - } - - GameObjectTemplate info = GetGameObjectTemplate(data.Id); - if (info == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {3}", - tableName, tmp.RespawnGameObject.GOGuid, data.Id, tmp.id); - continue; - } - - if (info.type == GameObjectTypes.FishingNode || info.type == GameObjectTypes.FishingHole || info.type == GameObjectTypes.Door || - info.type == GameObjectTypes.Button || info.type == GameObjectTypes.Trap) - { - Log.outError(LogFilter.Sql, "Table `{0}` have gameobject type ({1}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {2}", - tableName, info.entry, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {2}", + tableName, tmp.RespawnGameObject.GOGuid, tmp.id); + continue; } + GameObjectTemplate info = GetGameObjectTemplate(data.Id); + if (info == null) + { + Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {3}", + tableName, tmp.RespawnGameObject.GOGuid, data.Id, tmp.id); + continue; + } + + if (info.type == GameObjectTypes.FishingNode || info.type == GameObjectTypes.FishingHole || info.type == GameObjectTypes.Door || + info.type == GameObjectTypes.Button || info.type == GameObjectTypes.Trap) + { + Log.outError(LogFilter.Sql, "Table `{0}` have gameobject type ({1}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {2}", + tableName, info.entry, tmp.id); + continue; + } + break; + } + case ScriptCommands.TempSummonCreature: + { + if (!GridDefines.IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation)) { - if (!GridDefines.IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid coordinates (X: {1} Y: {2} Z: {3} O: {4}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {5}", - tableName, tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id); - continue; - } - - if (GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry) == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid creature (Entry: {1}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {2}", - tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid coordinates (X: {1} Y: {2} Z: {3} O: {4}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {5}", + tableName, tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id); + continue; } + if (GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry) == null) + { + Log.outError(LogFilter.Sql, "Table `{0}` has invalid creature (Entry: {1}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {2}", + tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id); + continue; + } + break; + } + case ScriptCommands.OpenDoor: case ScriptCommands.CloseDoor: + { + GameObjectData data = GetGameObjectData(tmp.ToggleDoor.GOGuid); + if (data == null) { - GameObjectData data = GetGameObjectData(tmp.ToggleDoor.GOGuid); - if (data == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in {2} for script id {3}", - tableName, tmp.ToggleDoor.GOGuid, tmp.command, tmp.id); - continue; - } - - GameObjectTemplate info = GetGameObjectTemplate(data.Id); - if (info == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in {3} for script id {4}", - tableName, tmp.ToggleDoor.GOGuid, data.Id, tmp.command, tmp.id); - continue; - } - - if (info.type != GameObjectTypes.Door) - { - Log.outError(LogFilter.Sql, "Table `{0}` has gameobject type ({1}) non supported by command {2} for script id {3}", - tableName, info.entry, tmp.command, tmp.id); - continue; - } - - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in {2} for script id {3}", + tableName, tmp.ToggleDoor.GOGuid, tmp.command, tmp.id); + continue; } + GameObjectTemplate info = GetGameObjectTemplate(data.Id); + if (info == null) + { + Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in {3} for script id {4}", + tableName, tmp.ToggleDoor.GOGuid, data.Id, tmp.command, tmp.id); + continue; + } + + if (info.type != GameObjectTypes.Door) + { + Log.outError(LogFilter.Sql, "Table `{0}` has gameobject type ({1}) non supported by command {2} for script id {3}", + tableName, info.entry, tmp.command, tmp.id); + continue; + } + + break; + } + case ScriptCommands.RemoveAura: + { + if (!Global.SpellMgr.HasSpellInfo(tmp.RemoveAura.SpellID, Difficulty.None)) { - if (!Global.SpellMgr.HasSpellInfo(tmp.RemoveAura.SpellID, Difficulty.None)) - { - Log.outError(LogFilter.Sql, "Table `{0}` using non-existent spell (id: {1}) in SCRIPT_COMMAND_REMOVE_AURA for script id {2}", - tableName, tmp.RemoveAura.SpellID, tmp.id); - continue; - } - if (Convert.ToBoolean((int)tmp.RemoveAura.Flags & ~0x1)) // 1 bits (0, 1) - { - Log.outError(LogFilter.Sql, "Table `{0}` using unknown flags in datalong2 ({1}) in SCRIPT_COMMAND_REMOVE_AURA for script id {2}", - tableName, tmp.RemoveAura.Flags, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` using non-existent spell (id: {1}) in SCRIPT_COMMAND_REMOVE_AURA for script id {2}", + tableName, tmp.RemoveAura.SpellID, tmp.id); + continue; } + if (Convert.ToBoolean((int)tmp.RemoveAura.Flags & ~0x1)) // 1 bits (0, 1) + { + Log.outError(LogFilter.Sql, "Table `{0}` using unknown flags in datalong2 ({1}) in SCRIPT_COMMAND_REMOVE_AURA for script id {2}", + tableName, tmp.RemoveAura.Flags, tmp.id); + continue; + } + break; + } case ScriptCommands.CastSpell: + { + if (!Global.SpellMgr.HasSpellInfo(tmp.CastSpell.SpellID, Difficulty.None)) { - if (!Global.SpellMgr.HasSpellInfo(tmp.CastSpell.SpellID, Difficulty.None)) - { - Log.outError(LogFilter.Sql, "Table `{0}` using non-existent spell (id: {1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", - tableName, tmp.CastSpell.SpellID, tmp.id); - continue; - } - if ((int)tmp.CastSpell.Flags > 4) // targeting type - { - Log.outError(LogFilter.Sql, "Table `{0}` using unknown target in datalong2 ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", - tableName, tmp.CastSpell.Flags, tmp.id); - continue; - } - if ((int)tmp.CastSpell.Flags != 4 && Convert.ToBoolean(tmp.CastSpell.CreatureEntry & ~0x1)) // 1 bit (0, 1) - { - Log.outError(LogFilter.Sql, "Table `{0}` using unknown flags in dataint ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", - tableName, tmp.CastSpell.CreatureEntry, tmp.id); - continue; - } - else if ((int)tmp.CastSpell.Flags == 4 && GetCreatureTemplate((uint)tmp.CastSpell.CreatureEntry) == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` using invalid creature entry in dataint ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", - tableName, tmp.CastSpell.CreatureEntry, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` using non-existent spell (id: {1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", + tableName, tmp.CastSpell.SpellID, tmp.id); + continue; } + if ((int)tmp.CastSpell.Flags > 4) // targeting type + { + Log.outError(LogFilter.Sql, "Table `{0}` using unknown target in datalong2 ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", + tableName, tmp.CastSpell.Flags, tmp.id); + continue; + } + if ((int)tmp.CastSpell.Flags != 4 && Convert.ToBoolean(tmp.CastSpell.CreatureEntry & ~0x1)) // 1 bit (0, 1) + { + Log.outError(LogFilter.Sql, "Table `{0}` using unknown flags in dataint ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", + tableName, tmp.CastSpell.CreatureEntry, tmp.id); + continue; + } + else if ((int)tmp.CastSpell.Flags == 4 && GetCreatureTemplate((uint)tmp.CastSpell.CreatureEntry) == null) + { + Log.outError(LogFilter.Sql, "Table `{0}` using invalid creature entry in dataint ({1}) in SCRIPT_COMMAND_CAST_SPELL for script id {2}", + tableName, tmp.CastSpell.CreatureEntry, tmp.id); + continue; + } + break; + } case ScriptCommands.CreateItem: + { + if (GetItemTemplate(tmp.CreateItem.ItemEntry) == null) { - if (GetItemTemplate(tmp.CreateItem.ItemEntry) == null) - { - Log.outError(LogFilter.Sql, "Table `{0}` has nonexistent item (entry: {1}) in SCRIPT_COMMAND_CREATE_ITEM for script id {2}", - tableName, tmp.CreateItem.ItemEntry, tmp.id); - continue; - } - if (tmp.CreateItem.Amount == 0) - { - Log.outError(LogFilter.Sql, "Table `{0}` SCRIPT_COMMAND_CREATE_ITEM but amount is {1} for script id {2}", - tableName, tmp.CreateItem.Amount, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has nonexistent item (entry: {1}) in SCRIPT_COMMAND_CREATE_ITEM for script id {2}", + tableName, tmp.CreateItem.ItemEntry, tmp.id); + continue; } + if (tmp.CreateItem.Amount == 0) + { + Log.outError(LogFilter.Sql, "Table `{0}` SCRIPT_COMMAND_CREATE_ITEM but amount is {1} for script id {2}", + tableName, tmp.CreateItem.Amount, tmp.id); + continue; + } + break; + } case ScriptCommands.PlayAnimkit: + { + if (!CliDB.AnimKitStorage.ContainsKey(tmp.PlayAnimKit.AnimKitID)) { - if (!CliDB.AnimKitStorage.ContainsKey(tmp.PlayAnimKit.AnimKitID)) - { - Log.outError(LogFilter.Sql, "Table `{0}` has invalid AnimKid id (datalong = {1}) in SCRIPT_COMMAND_PLAY_ANIMKIT for script id {2}", - tableName, tmp.PlayAnimKit.AnimKitID, tmp.id); - continue; - } - break; + Log.outError(LogFilter.Sql, "Table `{0}` has invalid AnimKid id (datalong = {1}) in SCRIPT_COMMAND_PLAY_ANIMKIT for script id {2}", + tableName, tmp.PlayAnimKit.AnimKitID, tmp.id); + continue; } + break; + } case ScriptCommands.FieldSetDeprecated: case ScriptCommands.FlagSetDeprecated: case ScriptCommands.FlagRemoveDeprecated: - { - Log.outError(LogFilter.Sql, $"Table `{tableName}` uses deprecated direct updatefield modify command {tmp.command} for script id {tmp.id}"); - continue; - } + { + Log.outError(LogFilter.Sql, $"Table `{tableName}` uses deprecated direct updatefield modify command {tmp.command} for script id {tmp.id}"); + continue; + } default: break; } @@ -1744,10 +1744,10 @@ namespace Game LoadCreatureTemplateModels(); // Checking needs to be done after loading because of the difficulty self referencing - foreach (var template in _creatureTemplateStorage.Values) + foreach (var template in creatureTemplateStorage.Values) CheckCreatureTemplate(template); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creature definitions in {1} ms", _creatureTemplateStorage.Count, Time.GetMSTimeDiffToNow(time)); + Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creature definitions in {1} ms", creatureTemplateStorage.Count, Time.GetMSTimeDiffToNow(time)); } public void LoadCreatureTemplate(SQLFields fields) { @@ -1809,27 +1809,36 @@ namespace Game creature.MaxGold = fields.Read(57); creature.AIName = fields.Read(58); creature.MovementType = fields.Read(59); - creature.InhabitType = (InhabitType)fields.Read(60); - creature.HoverHeight = fields.Read(61); - creature.ModHealth = fields.Read(62); - creature.ModHealthExtra = fields.Read(63); - creature.ModMana = fields.Read(64); - creature.ModManaExtra = fields.Read(65); - creature.ModArmor = fields.Read(66); - creature.ModDamage = fields.Read(67); - creature.ModExperience = fields.Read(68); - creature.RacialLeader = fields.Read(69); - creature.MovementId = fields.Read(70); - creature.CreatureDifficultyID = fields.Read(71); - creature.WidgetSetID = fields.Read(72); - creature.WidgetSetUnitConditionID = fields.Read(73); - creature.RegenHealth = fields.Read(74); - creature.MechanicImmuneMask = fields.Read(75); - creature.SpellSchoolImmuneMask = fields.Read(76); - creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(77); - creature.ScriptID = GetScriptId(fields.Read(78)); - _creatureTemplateStorage[entry] = creature; + if (!fields.IsNull(60)) + creature.Movement.Ground = (CreatureGroundMovementType)fields.Read(60); + + creature.Movement.Swim = fields.Read(61); + if (!fields.IsNull(62)) + creature.Movement.Flight = (CreatureFlightMovementType)fields.Read(62); + + + creature.Movement.Rooted = fields.Read(63); + creature.HoverHeight = fields.Read(64); + creature.ModHealth = fields.Read(65); + creature.ModHealthExtra = fields.Read(66); + creature.ModMana = fields.Read(67); + creature.ModManaExtra = fields.Read(68); + creature.ModArmor = fields.Read(69); + creature.ModDamage = fields.Read(70); + creature.ModExperience = fields.Read(71); + creature.RacialLeader = fields.Read(72); + creature.MovementId = fields.Read(73); + creature.CreatureDifficultyID = fields.Read(74); + creature.WidgetSetID = fields.Read(75); + creature.WidgetSetUnitConditionID = fields.Read(76); + creature.RegenHealth = fields.Read(77); + creature.MechanicImmuneMask = fields.Read(78); + creature.SpellSchoolImmuneMask = fields.Read(79); + creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(80); + creature.ScriptID = GetScriptId(fields.Read(81)); + + creatureTemplateStorage[entry] = creature; } void LoadCreatureTemplateModels() @@ -2120,7 +2129,7 @@ namespace Game uint item = result.Read(1); uint idx = result.Read(2); - if (!_creatureTemplateStorage.ContainsKey(entry)) + if (!creatureTemplateStorage.ContainsKey(entry)) { Log.outError(LogFilter.Sql, "Table `creature_questitem` has data for nonexistent creature (entry: {0}, idx: {1}), skipped", entry, idx); continue; @@ -2132,7 +2141,7 @@ namespace Game continue; } - _creatureQuestItemStorage.Add(entry, item); + creatureQuestItemStorage.Add(entry, item); ++count; } @@ -2224,6 +2233,42 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loaded {0} equipment templates in {1} ms", count, Time.GetMSTimeDiffToNow(time)); } + public void LoadCreatureMovementOverrides() + { + uint oldMSTime = Time.GetMSTime(); + + creatureMovementOverrides.Clear(); + + SQLResult result = DB.World.Query("SELECT SpawnId, Ground, Swim, Flight, Rooted from creature_movement_override"); + if (result.IsEmpty()) + { + Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!"); + return; + } + + do + { + ulong spawnId = result.Read(0); + if (GetCreatureData(spawnId) == null) + { + Log.outError(LogFilter.Sql, $"Creature (GUID: {spawnId}) does not exist but has a record in `creature_movement_override`"); + continue; + } + + CreatureMovementData movement = new(); + movement.Ground = (CreatureGroundMovementType)result.Read(1); + movement.Swim = result.Read(2); + movement.Flight = (CreatureFlightMovementType)result.Read(3); + movement.Rooted = result.Read(4); + + CheckCreatureMovement("creature_movement_override", spawnId, movement); + + creatureMovementOverrides[spawnId] = movement; + } + while (result.NextRow()); + + Log.outInfo(LogFilter.ServerLoading, $"Loaded {creatureMovementOverrides.Count} movement overrides in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); + } public void LoadCreatureClassLevelStats() { var time = Time.GetMSTime(); @@ -2258,7 +2303,7 @@ namespace Game ++count; } while (result.NextRow()); - foreach (var creatureTemplate in _creatureTemplateStorage.Values) + foreach (var creatureTemplate in creatureTemplateStorage.Values) { for (short lvl = creatureTemplate.Minlevel; lvl <= creatureTemplate.Maxlevel; ++lvl) { @@ -2352,7 +2397,7 @@ namespace Game uint entry = result.Read(0); Difficulty difficulty = (Difficulty)result.Read(1); - var template = _creatureTemplateStorage.LookupByKey(entry); + var template = creatureTemplateStorage.LookupByKey(entry); if (template == null) { Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_scaling`"); @@ -2395,20 +2440,20 @@ namespace Game for (uint diff2 = 0; diff2 < SharedConst.MaxCreatureDifficulties && ok2; ++diff2) { ok2 = false; - if (_difficultyEntries[diff2].Contains(cInfo.Entry)) + if (difficultyEntries[diff2].Contains(cInfo.Entry)) { Log.outError(LogFilter.Sql, "Creature (Entry: {0}) is listed as `difficulty_entry_{1}` of another creature, but itself lists {2} in `difficulty_entry_{3}`.", cInfo.Entry, diff2 + 1, cInfo.DifficultyEntry[diff], diff + 1); continue; } - if (_difficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff])) + if (difficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff])) { Log.outError(LogFilter.Sql, "Creature (Entry: {0}) already listed as `difficulty_entry_{1}` for another entry.", cInfo.DifficultyEntry[diff], diff2 + 1); continue; } - if (_hasDifficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff])) + if (hasDifficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff])) { Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has `difficulty_entry_{1}`={2} but creature entry {3} has itself a value in `difficulty_entry_{4}`.", cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff], cInfo.DifficultyEntry[diff], diff2 + 1); @@ -2532,8 +2577,8 @@ namespace Game continue; } - _hasDifficultyEntries[diff].Add(cInfo.Entry); - _difficultyEntries[diff].Add(cInfo.DifficultyEntry[diff]); + hasDifficultyEntries[diff].Add(cInfo.Entry); + difficultyEntries[diff].Add(cInfo.DifficultyEntry[diff]); ok = true; } @@ -2606,11 +2651,7 @@ namespace Game cInfo.Family = CreatureFamily.None; } - if (cInfo.InhabitType <= 0 || cInfo.InhabitType > InhabitType.Anywhere) - { - Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has wrong value ({1}) in `InhabitType`, creature will not correctly walk/swim/fly.", cInfo.Entry, cInfo.InhabitType); - cInfo.InhabitType = InhabitType.Anywhere; - } + CheckCreatureMovement("creature_template_movement", cInfo.Entry, cInfo.Movement); if (cInfo.HoverHeight < 0.0f) { @@ -2676,6 +2717,20 @@ namespace Game cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank); } + void CheckCreatureMovement(string table, ulong id, CreatureMovementData creatureMovement) + { + if (creatureMovement.Ground >= CreatureGroundMovementType.Max) + { + Log.outError(LogFilter.Sql, $"`{table}`.`Ground` wrong value ({creatureMovement.Ground}) for Id {id}, setting to Run."); + creatureMovement.Ground = CreatureGroundMovementType.Run; + } + + if (creatureMovement.Flight >= CreatureFlightMovementType.Max) + { + Log.outError(LogFilter.Sql, $"`{table}`.`Flight` wrong value ({creatureMovement.Flight}) for Id {id}, setting to None."); + creatureMovement.Flight = CreatureFlightMovementType.None; + } + } public void LoadLinkedRespawn() { uint oldMSTime = Time.GetMSTime(); @@ -2701,158 +2756,158 @@ namespace Game switch ((CreatureLinkedRespawnType)linkType) { case CreatureLinkedRespawnType.CreatureToCreature: + { + CreatureData slave = GetCreatureData(guidLow); + if (slave == null) { - CreatureData slave = GetCreatureData(guidLow); - if (slave == null) - { - Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", guidLow); - error = true; - break; - } - - CreatureData master = GetCreatureData(linkedGuidLow); - if (master == null) - { - Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", linkedGuidLow); - error = true; - break; - } - - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) - { - Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); - error = true; - break; - } - - // they must have a possibility to meet (normal/heroic difficulty) - if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) - { - Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); - error = true; - break; - } - - guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", guidLow); + error = true; break; } + + CreatureData master = GetCreatureData(linkedGuidLow); + if (master == null) + { + Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", linkedGuidLow); + error = true; + break; + } + + MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); + if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + { + Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); + error = true; + break; + } + + // they must have a possibility to meet (normal/heroic difficulty) + if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) + { + Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); + error = true; + break; + } + + guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + break; + } case CreatureLinkedRespawnType.CreatureToGO: + { + CreatureData slave = GetCreatureData(guidLow); + if (slave == null) { - CreatureData slave = GetCreatureData(guidLow); - if (slave == null) - { - Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", guidLow); - error = true; - break; - } - - GameObjectData master = GetGameObjectData(linkedGuidLow); - if (master == null) - { - Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow); - error = true; - break; - } - - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) - { - Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); - error = true; - break; - } - - // they must have a possibility to meet (normal/heroic difficulty) - if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) - { - Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); - error = true; - break; - } - - guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", guidLow); + error = true; break; } + + GameObjectData master = GetGameObjectData(linkedGuidLow); + if (master == null) + { + Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow); + error = true; + break; + } + + MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); + if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + { + Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); + error = true; + break; + } + + // they must have a possibility to meet (normal/heroic difficulty) + if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) + { + Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); + error = true; + break; + } + + guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + break; + } case CreatureLinkedRespawnType.GOToGO: + { + GameObjectData slave = GetGameObjectData(guidLow); + if (slave == null) { - GameObjectData slave = GetGameObjectData(guidLow); - if (slave == null) - { - Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow); - error = true; - break; - } - - GameObjectData master = GetGameObjectData(linkedGuidLow); - if (master == null) - { - Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow); - error = true; - break; - } - - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) - { - Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); - error = true; - break; - } - - // they must have a possibility to meet (normal/heroic difficulty) - if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) - { - Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); - error = true; - break; - } - - guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow); + error = true; break; } + + GameObjectData master = GetGameObjectData(linkedGuidLow); + if (master == null) + { + Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow); + error = true; + break; + } + + MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); + if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + { + Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); + error = true; + break; + } + + // they must have a possibility to meet (normal/heroic difficulty) + if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) + { + Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); + error = true; + break; + } + + guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + break; + } case CreatureLinkedRespawnType.GOToCreature: + { + GameObjectData slave = GetGameObjectData(guidLow); + if (slave == null) { - GameObjectData slave = GetGameObjectData(guidLow); - if (slave == null) - { - Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow); - error = true; - break; - } - - CreatureData master = GetCreatureData(linkedGuidLow); - if (master == null) - { - Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", linkedGuidLow); - error = true; - break; - } - - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) - { - Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); - error = true; - break; - } - - // they must have a possibility to meet (normal/heroic difficulty) - if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) - { - Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); - error = true; - break; - } - - guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow); + error = true; break; } + CreatureData master = GetCreatureData(linkedGuidLow); + if (master == null) + { + Log.outError(LogFilter.Sql, "Couldn't get creature data for GUIDLow {0}", linkedGuidLow); + error = true; + break; + } + + MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); + if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + { + Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); + error = true; + break; + } + + // they must have a possibility to meet (normal/heroic difficulty) + if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any()) + { + Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow); + error = true; + break; + } + + guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + break; + } + } if (!error) @@ -2865,7 +2920,7 @@ namespace Game { uint oldMSTime = Time.GetMSTime(); - _npcTextStorage.Clear(); + npcTextStorage.Clear(); SQLResult result = DB.World.Query("SELECT ID, Probability0, Probability1, Probability2, Probability3, Probability4, Probability5, Probability6, Probability7, " + "BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7 FROM npc_text"); @@ -2912,10 +2967,10 @@ namespace Game npcText.Data[i].Probability = 0; } } - _npcTextStorage[textID] = npcText; + npcTextStorage[textID] = npcText; } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} npc texts in {1} ms", _npcTextStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime)); + Log.outInfo(LogFilter.ServerLoading, "Loaded {0} npc texts in {1} ms", npcTextStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } public void LoadTrainers() @@ -2923,7 +2978,7 @@ namespace Game uint oldMSTime = Time.GetMSTime(); // For reload case - _trainers.Clear(); + trainers.Clear(); MultiMap spellsByTrainer = new(); SQLResult trainerSpellsResult = DB.World.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"); @@ -2990,7 +3045,7 @@ namespace Game spellsByTrainer.Remove(trainerId); } - _trainers.Add(trainerId, new Trainer(trainerId, trainerType, greeting, spells)); + trainers.Add(trainerId, new Trainer(trainerId, trainerType, greeting, spells)); } while (trainersResult.NextRow()); } @@ -3013,7 +3068,7 @@ namespace Game if (!SharedConst.IsValidLocale(locale) || locale == Locale.enUS) continue; - Trainer trainer = _trainers.LookupByKey(trainerId); + Trainer trainer = trainers.LookupByKey(trainerId); if (trainer != null) trainer.AddGreetingLocale(locale, trainerLocalesResult.Read(2)); else @@ -3022,7 +3077,7 @@ namespace Game } while (trainerLocalesResult.NextRow()); } - Log.outInfo(LogFilter.ServerLoading, $"Loaded {_trainers.Count} Trainers in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); + Log.outInfo(LogFilter.ServerLoading, $"Loaded {trainers.Count} Trainers in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public void LoadCreatureTrainers() { @@ -3234,7 +3289,7 @@ namespace Game CreatureData data = new(); data.spawnId = guid; data.Id = entry; - data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6)); + data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6)); data.displayid = result.Read(7); data.equipmentId = result.Read(8); data.spawntimesecs = result.Read(9); @@ -3274,7 +3329,7 @@ namespace Game bool ok = true; for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff) { - if (_difficultyEntries[diff].Contains(data.Id)) + if (difficultyEntries[diff].Contains(data.Id)) { Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that listed as difficulty {1} template (entry: {2}) in `creaturetemplate`, skipped.", guid, diff + 1, data.Id); ok = false; @@ -3471,7 +3526,7 @@ namespace Game } public List GetCreatureQuestItemList(uint id) { - return _creatureQuestItemStorage.LookupByKey(id); + return creatureQuestItemStorage.LookupByKey(id); } public CreatureAddon GetCreatureAddon(ulong lowguid) { @@ -3479,7 +3534,7 @@ namespace Game } public CreatureTemplate GetCreatureTemplate(uint entry) { - return _creatureTemplateStorage.LookupByKey(entry); + return creatureTemplateStorage.LookupByKey(entry); } public CreatureAddon GetCreatureTemplateAddon(uint entry) { @@ -3495,7 +3550,7 @@ namespace Game } public Dictionary GetCreatureTemplates() { - return _creatureTemplateStorage; + return creatureTemplateStorage; } public Dictionary GetAllCreatureData() { return creatureDataStorage; } public CreatureData GetCreatureData(ulong guid) @@ -3573,7 +3628,7 @@ namespace Game RemoveCreatureFromGrid(guid, data); OnDeleteSpawnData(data); } - + creatureDataStorage.Remove(guid); } public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass) @@ -3608,7 +3663,7 @@ namespace Game }); if (creatureModel != null) - model = creatureModel; + model = creatureModel; } return minfotmp; } @@ -3622,7 +3677,7 @@ namespace Game } public NpcText GetNpcText(uint textId) { - return _npcTextStorage.LookupByKey(textId); + return npcTextStorage.LookupByKey(textId); } //GameObjects @@ -3759,18 +3814,18 @@ namespace Game CheckGOLockId(got, got.Camera.open, 0); break; case GameObjectTypes.MapObjTransport: //15 + { + if (got.MoTransport.taxiPathID != 0) { - if (got.MoTransport.taxiPathID != 0) - { - if (got.MoTransport.taxiPathID >= CliDB.TaxiPathNodesByPath.Count || CliDB.TaxiPathNodesByPath[got.MoTransport.taxiPathID].Empty()) - Log.outError(LogFilter.Sql, "GameObject (Entry: {0} GoType: {1}) have data0={2} but TaxiPath (Id: {3}) not exist.", - entry, got.type, got.MoTransport.taxiPathID, got.MoTransport.taxiPathID); - } - int transportMap = got.MoTransport.SpawnMap; - if (transportMap != 0) - _transportMaps.Add((ushort)transportMap); - break; + if (got.MoTransport.taxiPathID >= CliDB.TaxiPathNodesByPath.Count || CliDB.TaxiPathNodesByPath[got.MoTransport.taxiPathID].Empty()) + Log.outError(LogFilter.Sql, "GameObject (Entry: {0} GoType: {1}) have data0={2} but TaxiPath (Id: {3}) not exist.", + entry, got.type, got.MoTransport.taxiPathID, got.MoTransport.taxiPathID); } + int transportMap = got.MoTransport.SpawnMap; + if (transportMap != 0) + _transportMaps.Add((ushort)transportMap); + break; + } case GameObjectTypes.SpellCaster: //22 // always must have spell CheckGOSpellId(got, got.SpellCaster.spell, 0); @@ -3799,12 +3854,12 @@ namespace Game } break; case GameObjectTypes.GarrisonBuilding: - { - int transportMap = got.garrisonBuilding.SpawnMap; - if (transportMap != 0) - _transportMaps.Add((ushort)transportMap); - } - break; + { + int transportMap = got.garrisonBuilding.SpawnMap; + if (transportMap != 0) + _transportMaps.Add((ushort)transportMap); + } + break; case GameObjectTypes.GatheringNode: if (got.GatheringNode.open != 0) CheckGOLockId(got, got.GatheringNode.open, 0); @@ -4279,30 +4334,30 @@ namespace Game case GameObjectTypes.QuestGiver: break; case GameObjectTypes.Chest: - { - // scan GO chest with loot including quest items - uint lootId = pair.Value.GetLootId(); + { + // scan GO chest with loot including quest items + uint lootId = pair.Value.GetLootId(); - // find quest loot for GO - if (pair.Value.Chest.questID != 0 || Loots.LootStorage.Gameobject.HaveQuestLootFor(lootId)) - break; + // find quest loot for GO + if (pair.Value.Chest.questID != 0 || Loots.LootStorage.Gameobject.HaveQuestLootFor(lootId)) + break; - continue; - } + continue; + } case GameObjectTypes.Generic: - { - if (pair.Value.Generic.questID > 0) //quests objects - break; + { + if (pair.Value.Generic.questID > 0) //quests objects + break; - continue; - } + continue; + } case GameObjectTypes.Goober: - { - if (pair.Value.Goober.questID > 0) //quests objects - break; + { + if (pair.Value.Goober.questID > 0) //quests objects + break; - continue; - } + continue; + } default: continue; } @@ -4395,7 +4450,7 @@ namespace Game { GameObjectData data = GetGameObjectData(guid); if (data != null) - { + { RemoveGameObjectFromGrid(guid, data); OnDeleteSpawnData(data); } @@ -4760,7 +4815,7 @@ namespace Game } public Trainer GetTrainer(uint trainerId) { - return _trainers.LookupByKey(trainerId); + return trainers.LookupByKey(trainerId); } public void AddVendorItem(uint entry, VendorItem vItem, bool persist = true) { @@ -4909,6 +4964,10 @@ namespace Game { return cacheVendorItemStorage.LookupByKey(entry); } + public CreatureMovementData GetCreatureMovementOverride(ulong spawnId) + { + return creatureMovementOverrides.lookupbykey(spawnId); + } public EquipmentInfo GetEquipmentInfo(uint entry, int id) { var equip = equipmentInfoStorage.LookupByKey(entry); @@ -5196,27 +5255,27 @@ namespace Game switch (creditType) { case EncounterCreditType.KillCreature: + { + CreatureTemplate creatureInfo = GetCreatureTemplate(creditEntry); + if (creatureInfo == null) { - CreatureTemplate creatureInfo = GetCreatureTemplate(creditEntry); - if (creatureInfo == null) + Log.outError(LogFilter.Sql, "Table `instance_encounters` has an invalid creature (entry {0}) linked to the encounter {1} ({2}), skipped!", + creditEntry, entry, dungeonEncounter.Name[Global.WorldMgr.GetDefaultDbcLocale()]); + continue; + } + creatureInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss; + for (byte diff = 0; diff < SharedConst.MaxCreatureDifficulties; ++diff) + { + uint diffEntry = creatureInfo.DifficultyEntry[diff]; + if (diffEntry != 0) { - Log.outError(LogFilter.Sql, "Table `instance_encounters` has an invalid creature (entry {0}) linked to the encounter {1} ({2}), skipped!", - creditEntry, entry, dungeonEncounter.Name[Global.WorldMgr.GetDefaultDbcLocale()]); - continue; + CreatureTemplate diffInfo = GetCreatureTemplate(diffEntry); + if (diffInfo != null) + diffInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss; } - creatureInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss; - for (byte diff = 0; diff < SharedConst.MaxCreatureDifficulties; ++diff) - { - uint diffEntry = creatureInfo.DifficultyEntry[diff]; - if (diffEntry != 0) - { - CreatureTemplate diffInfo = GetCreatureTemplate(diffEntry); - if (diffInfo != null) - diffInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss; - } } break; - } + } case EncounterCreditType.CastSpell: if (!Global.SpellMgr.HasSpellInfo(creditEntry, Difficulty.None)) { @@ -5520,7 +5579,7 @@ namespace Game return null; } public List GetSpawnGroupsForInstance(uint instanceId) { return _instanceSpawnGroupStorage.LookupByKey(instanceId); } - + //Player public void LoadPlayerInfo() { @@ -5666,7 +5725,7 @@ namespace Game } } } - } + } Log.outInfo(LogFilter.ServerLoading, "Loading Player Create Items Override Data..."); { // 0 1 2 3 @@ -7870,8 +7929,8 @@ namespace Game public List GetExclusiveQuestGroupBounds(int exclusiveGroupId) { return _exclusiveQuestGroups.LookupByKey(exclusiveGroupId); - } - + } + //Spells /Skills / Phases public void LoadPhases() { @@ -7894,7 +7953,7 @@ namespace Game public void UnloadPhaseConditions() { foreach (var pair in _phaseInfoByArea) - pair.Value.Conditions.Clear(); + pair.Value.Conditions.Clear(); } void LoadTerrainWorldMaps() { @@ -8410,7 +8469,7 @@ namespace Game uint oldMSTime = Time.GetMSTime(); _questRequestItemsLocaleStorage.Clear(); // need for reload case - // 0 1 2 + // 0 1 2 SQLResult result = DB.World.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale"); if (result.IsEmpty()) return; @@ -9664,7 +9723,7 @@ namespace Game // Initialize Query data for creatures if (mask.HasAnyFlag(QueryDataGroup.Creatures)) - foreach (var creaturePair in _creatureTemplateStorage) + foreach (var creaturePair in creatureTemplateStorage) creaturePair.Value.InitializeQueryData(); // Initialize Query Data for gameobjects @@ -10062,7 +10121,7 @@ namespace Game public List GetSummonGroup(uint summonerId, SummonerType summonerType, byte group) { - Tuple key = Tuple.Create(summonerId, summonerType, group); + Tuple key = Tuple.Create(summonerId, summonerType, group); return _tempSummonDataStorage.LookupByKey(key); } @@ -10282,21 +10341,22 @@ namespace Game Dictionary pointsOfInterestStorage = new(); //Creature - Dictionary _creatureTemplateStorage = new(); + Dictionary creatureTemplateStorage = new(); Dictionary creatureModelStorage = new(); Dictionary creatureDataStorage = new(); Dictionary creatureAddonStorage = new(); - MultiMap _creatureQuestItemStorage = new(); + MultiMap creatureQuestItemStorage = new(); Dictionary creatureTemplateAddonStorage = new(); + Dictionary creatureMovementOverrides = new(); MultiMap> equipmentInfoStorage = new(); Dictionary linkedRespawnStorage = new(); Dictionary creatureBaseStatsStorage = new(); Dictionary cacheVendorItemStorage = new(); - Dictionary _trainers = new(); + Dictionary trainers = new(); Dictionary<(uint creatureId, uint gossipMenuId, uint gossipOptionIndex), uint> _creatureDefaultTrainers = new(); - List[] _difficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate - List[] _hasDifficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate - Dictionary _npcTextStorage = new(); + List[] difficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate + List[] hasDifficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate + Dictionary npcTextStorage = new(); //GameObject Dictionary _gameObjectTemplateStorage = new(); @@ -10689,7 +10749,7 @@ namespace Game public uint SpawnGroupId; public InstanceSpawnGroupFlags Flags; } - + public class SpellClickInfo { public uint spellId; @@ -10762,7 +10822,7 @@ namespace Game public List Points; public bool AlwaysAllowMergingBlobs; - public QuestPOIBlobData(int blobIndex, int objectiveIndex, int questObjectiveID, int questObjectID, int mapID, int uiMapID, int priority, int flags, + public QuestPOIBlobData(int blobIndex, int objectiveIndex, int questObjectiveID, int questObjectID, int mapID, int uiMapID, int priority, int flags, int worldEffectID, int playerConditionID, int navigationPlayerConditionID, int spawnTrackingID, List points, bool alwaysAllowMergingBlobs) { BlobIndex = blobIndex; @@ -10779,11 +10839,11 @@ namespace Game SpawnTrackingID = spawnTrackingID; Points = points; AlwaysAllowMergingBlobs = alwaysAllowMergingBlobs; - } + } } public class QuestPOIBlobPoint - { + { public int X; public int Y; public int Z; diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs index 18db343cb..f7ab832d9 100644 --- a/Source/Game/Server/WorldManager.cs +++ b/Source/Game/Server/WorldManager.cs @@ -616,6 +616,9 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loading Creature Addon Data..."); Global.ObjectMgr.LoadCreatureAddons(); + Log.outInfo(LogFilter.ServerLoading, "Loading Creature Movement Overrides..."); + Global.ObjectMgr.LoadCreatureMovementOverrides(); // must be after LoadCreatures() + Log.outInfo(LogFilter.ServerLoading, "Loading GameObjects..."); Global.ObjectMgr.LoadGameObjects(); diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index c30d7d2c7..b8a1f658b 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -1198,6 +1198,7 @@ INSERT INTO `rbac_linked_permissions` VALUES (196,870), (196,871), (196,872), +(196,873), (196,881), (197,232), (197,236), @@ -2130,6 +2131,7 @@ INSERT INTO `rbac_permissions` VALUES (870,'Command: debug threatinfo'), (871,'Command: debug instancespawn'), (872,'Command: server debug'), +(873,'Command: reload creature_movement_override'), (881,'Command: reload vehicle_template'); /*!40000 ALTER TABLE `rbac_permissions` ENABLE KEYS */; UNLOCK TABLES; @@ -2347,6 +2349,7 @@ INSERT INTO `updates` VALUES ('2018_02_18_00_auth.sql','8489DD3EFFE14A7486B593435F0BA2BC69B6EABF','ARCHIVED','2018-02-18 16:35:55',0), ('2018_02_19_00_auth.sql','07CE658C5EF88693D3C047EF8E724F94ADA74C15','ARCHIVED','2018-02-19 22:33:32',0), ('2018_02_28_00_auth.sql','E92EF4ABF7FA0C66649E1633DD0459F44C09EB83','ARCHIVED','2018-02-28 23:07:59',0), +('2018_03_08_00_auth.sql','624C58A07E0B4DDC4C1347E8BA8EFEEFD5B43DA7','RELEASED','2018-01-12 15:10:11',0), ('2018_03_14_00_auth.sql','2D71E93DF7419A30D0D21D8A80CF05698302575A','ARCHIVED','2018-03-14 23:07:59',0), ('2018_04_06_00_auth.sql','D8416F0C4751763202B1997C81423F6EE2FCF9A6','ARCHIVED','2018-04-06 18:00:32',0), ('2018_05_13_00_auth.sql','A9E20F2EB1E2FDBB982DB6B00DB7301852B27CD4','ARCHIVED','2018-05-13 20:22:32',0), diff --git a/sql/updates/auth/master/2018_03_08_00_auth.sql b/sql/updates/auth/master/2018_03_08_00_auth.sql new file mode 100644 index 000000000..8b0e18b1e --- /dev/null +++ b/sql/updates/auth/master/2018_03_08_00_auth.sql @@ -0,0 +1,7 @@ +DELETE FROM `rbac_permissions` WHERE `id` = 873; +INSERT INTO `rbac_permissions` (`id`,`name`) VALUES +(873, 'Command: reload creature_movement_override'); + +DELETE FROM `rbac_linked_permissions` WHERE `linkedId` = 873; +INSERT INTO `rbac_linked_permissions` (`id`,`linkedId`) VALUES +(196, 873); diff --git a/sql/updates/world/master/2021_08_24_00_world_2018_03_08_11_world.sql b/sql/updates/world/master/2021_08_24_00_world_2018_03_08_11_world.sql new file mode 100644 index 000000000..d805be787 --- /dev/null +++ b/sql/updates/world/master/2021_08_24_00_world_2018_03_08_11_world.sql @@ -0,0 +1,34 @@ +DROP TABLE IF EXISTS `creature_template_movement`; +CREATE TABLE `creature_template_movement` ( + `CreatureId` int(10) unsigned NOT NULL DEFAULT '0', + `Ground` tinyint(3) unsigned NOT NULL DEFAULT '1', + `Swim` tinyint(3) unsigned NOT NULL DEFAULT '1', + `Flight` tinyint(3) unsigned NOT NULL DEFAULT '0', + `Rooted` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`CreatureId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `creature_movement_override`; +CREATE TABLE `creature_movement_override` ( + `SpawnId` bigint(20) unsigned NOT NULL DEFAULT '0', + `Ground` tinyint(3) unsigned NOT NULL DEFAULT '1', + `Swim` tinyint(3) unsigned NOT NULL DEFAULT '1', + `Flight` tinyint(3) unsigned NOT NULL DEFAULT '0', + `Rooted` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`SpawnId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `creature_template_movement` SELECT `entry`,0,0,0,0 FROM `creature_template` WHERE `InhabitType`!=3; +UPDATE `creature_template_movement` SET `Ground`=1 WHERE `CreatureId` IN (SELECT `entry` FROM `creature_template` WHERE `InhabitType` & 1); +UPDATE `creature_template_movement` SET `Swim`=1 WHERE `CreatureId` IN (SELECT `entry` FROM `creature_template` WHERE `InhabitType` & 2); +UPDATE `creature_template_movement` SET `Flight`=1 WHERE `CreatureId` IN (SELECT `entry` FROM `creature_template` WHERE (`InhabitType` & 5) = 4); +UPDATE `creature_template_movement` SET `Flight`=2 WHERE `CreatureId` IN (SELECT `entry` FROM `creature_template` WHERE (`InhabitType` & 5) = 5); +UPDATE `creature_template_movement` SET `Rooted`=1 WHERE `CreatureId` IN (SELECT `entry` FROM `creature_template` WHERE `InhabitType` & 8); + +ALTER TABLE `creature_template` DROP `InhabitType`; + +UPDATE `trinity_string` SET `content_default`='Movement type: %s' WHERE `entry`=11008; + +DELETE FROM `command` WHERE `name`='reload creature_movement_override'; +INSERT INTO `command` (`name`,`permission`,`help`) VALUES +('reload creature_movement_override',873,'Syntax: .reload creature_movement_override\nReload creature_movement_override table.');