From 771cbadc69578aadd94cf7486fab3ff77ab29d1f Mon Sep 17 00:00:00 2001 From: hondacrx Date: Mon, 5 Feb 2024 17:22:55 -0500 Subject: [PATCH] Core/Waypoints: Refactor to split data into path and node related info in db Port From (https://github.com/TrinityCore/TrinityCore/commit/12186ef8573f60abeff4747da58767ee71092600) --- Source/Framework/Constants/AccountConst.cs | 2 +- .../Database/Databases/WorldDatabase.cs | 55 +- Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs | 30 +- Source/Game/AI/SmartScripts/SmartAI.cs | 24 +- Source/Game/AI/SmartScripts/SmartAIManager.cs | 2 +- Source/Game/AI/SmartScripts/SmartScript.cs | 12 +- Source/Game/Chat/Commands/ReloadCommand.cs | 8 +- Source/Game/Chat/Commands/WPCommands.cs | 541 ++++-------------- Source/Game/Entities/Creature/Creature.cs | 8 +- Source/Game/Entities/Creature/CreatureData.cs | 2 +- Source/Game/Globals/ObjectManager.cs | 14 +- .../Movement/Generators/WaypointMovement.cs | 89 +-- Source/Game/Movement/MotionMaster.cs | 2 +- Source/Game/Movement/WaypointManager.cs | 380 ++++++++---- Source/Game/World/WorldManager.cs | 2 +- 15 files changed, 520 insertions(+), 651 deletions(-) diff --git a/Source/Framework/Constants/AccountConst.cs b/Source/Framework/Constants/AccountConst.cs index d9e7464e2..5aa0a0e83 100644 --- a/Source/Framework/Constants/AccountConst.cs +++ b/Source/Framework/Constants/AccountConst.cs @@ -569,7 +569,7 @@ namespace Framework.Constants CommandReloadSpellGroupStackRules = 703, CommandReloadCypherString = 704, // 705 - 706 previously used, do not reuse - CommandReloadWaypointData = 707, + CommandReloadWaypointPath = 707, CommandReloadVehicleAccesory = 708, CommandReloadVehicleTemplateAccessory = 709, CommandReset = 710, diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index b8a5ad7dd..b7e7c0c03 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -28,19 +28,20 @@ namespace Framework.Database PrepareStatement(WorldStatements.UPD_CREATURE_WANDER_DISTANCE, "UPDATE creature SET wander_distance = ?, MovementType = ? WHERE guid = ?"); PrepareStatement(WorldStatements.UPD_CREATURE_SPAWN_TIME_SECS, "UPDATE creature SET spawntimesecs = ? WHERE guid = ?"); PrepareStatement(WorldStatements.INS_CREATURE_FORMATION, "INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES (?, ?, ?, ?, ?)"); - PrepareStatement(WorldStatements.INS_WAYPOINT_DATA, "INSERT INTO waypoint_data (id, point, position_x, position_y, position_z, orientation) VALUES (?, ?, ?, ?, ?, ?)"); - PrepareStatement(WorldStatements.DEL_WAYPOINT_DATA, "DELETE FROM waypoint_data WHERE id = ? AND point = ?"); - PrepareStatement(WorldStatements.UPD_WAYPOINT_DATA_POINT, "UPDATE waypoint_data SET point = point - 1 WHERE id = ? AND point > ?"); - PrepareStatement(WorldStatements.UPD_WAYPOINT_DATA_POSITION, "UPDATE waypoint_data SET position_x = ?, position_y = ?, position_z = ?, orientation = ? where id = ? AND point = ?"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay FROM waypoint_data WHERE id = ? ORDER BY point"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ?"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE point = 1 AND id = ?"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1"); - PrepareStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_POS, "SELECT id, point FROM waypoint_data WHERE (abs(position_x - ?) <= ?) and (abs(position_y - ?) <= ?) and (abs(position_z - ?) <= ?)"); - PrepareStatement(WorldStatements.INS_CREATURE_ADDON, "INSERT INTO creature_addon(guid, path_id) VALUES (?, ?)"); - PrepareStatement(WorldStatements.UPD_CREATURE_ADDON_PATH, "UPDATE creature_addon SET path_id = ? WHERE guid = ?"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_BY_PATHID, "SELECT PathId, MoveType, Flags FROM waypoint_path WHERE PathId = ?"); + PrepareStatement(WorldStatements.INS_WAYPOINT_PATH_NODE, "INSERT INTO waypoint_path_node (PathId, NodeId, PositionX, PositionY, PositionZ, Orientation) VALUES (?, ?, ?, ?, ?, ?)"); + PrepareStatement(WorldStatements.DEL_WAYPOINT_PATH_NODE, "DELETE FROM waypoint_path_node WHERE PathId = ? AND NodeId = ?"); + PrepareStatement(WorldStatements.UPD_WAYPOINT_PATH_NODE, "UPDATE waypoint_path_node SET NodeId = NodeId - 1 WHERE PathId = ? AND NodeId > ?"); + PrepareStatement(WorldStatements.UPD_WAYPOINT_PATH_NODE_POSITION, "UPDATE waypoint_path_node SET PositionX = ?, PositionY = ?, PositionZ = ?, Orientation = ? WHERE PathId = ? AND NodeId = ?"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_MAX_PATHID, "SELECT MAX(PathId) FROM waypoint_path_node"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_MAX_NODEID, "SELECT MAX(NodeId) FROM waypoint_path_node WHERE PathId = ?"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_BY_PATHID, "SELECT PathId, NodeId, PositionX, PositionY, PositionZ, Orientation, Delay FROM waypoint_path_node WHERE PathId = ? ORDER BY NodeId"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_POS_BY_PATHID, "SELECT NodeId, PositionX, PositionY, PositionZ, Orientation FROM waypoint_path_node WHERE PathId = ?"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_POS_FIRST_BY_PATHID, "SELECT PositionX, PositionY, PositionZ, Orientation FROM waypoint_path_node WHERE NodeId = 1 AND PathId = ?"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_POS_LAST_BY_PATHID, "SELECT PositionX, PositionY, PositionZ, Orientation FROM waypoint_path_node WHERE PathId = ? ORDER BY NodeId DESC LIMIT 1"); + PrepareStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_BY_POS, "SELECT PathId, NodeId FROM waypoint_path_node WHERE (abs(PositionX - ?) <= ?) and (abs(PositionY - ?) <= ?) and (abs(PositionZ - ?) <= ?)"); + PrepareStatement(WorldStatements.INS_CREATURE_ADDON, "INSERT INTO creature_addon(guid, PathId) VALUES (?, ?)"); + PrepareStatement(WorldStatements.UPD_CREATURE_ADDON_PATH, "UPDATE creature_addon SET PathId = ? WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE_ADDON, "DELETE FROM creature_addon WHERE guid = ?"); PrepareStatement(WorldStatements.SEL_CREATURE_ADDON_BY_GUID, "SELECT guid FROM creature_addon WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?"); @@ -88,22 +89,18 @@ namespace Framework.Database UPD_CREATURE_WANDER_DISTANCE, UPD_CREATURE_SPAWN_TIME_SECS, INS_CREATURE_FORMATION, - INS_WAYPOINT_DATA, - DEL_WAYPOINT_DATA, - UPD_WAYPOINT_DATA_POINT, - UPD_WAYPOINT_DATA_POSITION, - UPD_WAYPOINT_DATA_WPGUID, - UPD_WAYPOINT_DATA_ALL_WPGUID, - SEL_WAYPOINT_DATA_MAX_ID, - SEL_WAYPOINT_DATA_BY_ID, - SEL_WAYPOINT_DATA_POS_BY_ID, - SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, - SEL_WAYPOINT_DATA_POS_LAST_BY_ID, - SEL_WAYPOINT_DATA_BY_WPGUID, - SEL_WAYPOINT_DATA_ALL_BY_WPGUID, - SEL_WAYPOINT_DATA_MAX_POINT, - SEL_WAYPOINT_DATA_BY_POS, - SEL_WAYPOINT_DATA_WPGUID_BY_ID, + SEL_WAYPOINT_PATH_BY_PATHID, + INS_WAYPOINT_PATH_NODE, + DEL_WAYPOINT_PATH_NODE, + UPD_WAYPOINT_PATH_NODE, + UPD_WAYPOINT_PATH_NODE_POSITION, + SEL_WAYPOINT_PATH_NODE_MAX_PATHID, + SEL_WAYPOINT_PATH_NODE_BY_PATHID, + SEL_WAYPOINT_PATH_NODE_POS_BY_PATHID, + SEL_WAYPOINT_PATH_NODE_POS_FIRST_BY_PATHID, + SEL_WAYPOINT_PATH_NODE_POS_LAST_BY_PATHID, + SEL_WAYPOINT_PATH_NODE_MAX_NODEID, + SEL_WAYPOINT_PATH_NODE_BY_POS, UPD_CREATURE_ADDON_PATH, INS_CREATURE_ADDON, DEL_CREATURE_ADDON, diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs index c0b807c6f..0ccc8d802 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs @@ -295,13 +295,13 @@ namespace Game.AI } else if (moveType == MovementGeneratorType.Waypoint) { - Cypher.Assert(Id < _path.nodes.Count, $"EscortAI::MovementInform: referenced movement id ({Id}) points to non-existing node in loaded path ({me.GetGUID()})"); - WaypointNode waypoint = _path.nodes[(int)Id]; + Cypher.Assert(Id < _path.Nodes.Count, $"EscortAI::MovementInform: referenced movement id ({Id}) points to non-existing node in loaded path ({me.GetGUID()})"); + WaypointNode waypoint = _path.Nodes[(int)Id]; - Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::MovementInform: waypoint node {waypoint.id} reached ({me.GetGUID()})"); + Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::MovementInform: waypoint node {waypoint.Id} reached ({me.GetGUID()})"); // last point - if (Id == _path.nodes.Count - 1) + if (Id == _path.Nodes.Count - 1) { _started = false; _ended = true; @@ -310,7 +310,7 @@ namespace Game.AI } } - void AddWaypoint(uint id, float x, float y, float z, bool run) + public void AddWaypoint(uint id, float x, float y, float z, bool run) { AddWaypoint(id, x, y, z, 0.0f, TimeSpan.Zero, run); } @@ -320,20 +320,14 @@ namespace Game.AI GridDefines.NormalizeMapCoord(ref x); GridDefines.NormalizeMapCoord(ref y); - WaypointNode waypoint = new(); - waypoint.id = id; - waypoint.x = x; - waypoint.y = y; - waypoint.z = z; - waypoint.orientation = orientation; - waypoint.moveType = run ? WaypointMoveType.Run : WaypointMoveType.Walk; - waypoint.delay = (uint)waitTime.TotalMilliseconds; - _path.nodes.Add(waypoint); + WaypointNode waypoint = new(id, x, y, z, orientation, (uint)waitTime.TotalMilliseconds); + waypoint.MoveType = run ? WaypointMoveType.Run : WaypointMoveType.Walk; + _path.Nodes.Add(waypoint); } void ResetPath() { - _path.nodes.Clear(); + _path.Nodes.Clear(); } public void LoadPath(uint pathId) @@ -350,7 +344,7 @@ namespace Game.AI /// todo get rid of this many variables passed in function. public void Start(bool isActiveAttacker = true, ObjectGuid playerGUID = default, Quest quest = null, bool instantRespawn = false, bool canLoopPath = false) { - if (_path.nodes.Empty()) + if (_path.Nodes.Empty()) { Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()}) path is empty ({me.GetGUID()})"); return; @@ -376,7 +370,7 @@ namespace Game.AI return; } - if (_path.nodes.Empty()) + if (_path.Nodes.Empty()) { Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()} starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)} ({me.GetGUID()})"); return; @@ -404,7 +398,7 @@ namespace Game.AI me.SetImmuneToNPC(false); } - Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()}, started with {_path.nodes.Count} waypoints. ActiveAttacker = {_activeAttacker}, Player = {_playerGUID} ({me.GetGUID()})"); + Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()}, started with {_path.Nodes.Count} waypoints. ActiveAttacker = {_activeAttacker}, Player = {_playerGUID} ({me.GetGUID()})"); _started = false; AddEscortState(EscortState.Escorting); diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 1d099a337..d4bee673c 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -33,7 +33,7 @@ namespace Game.AI SmartEscortState _escortState; uint _escortNPCFlags; uint _escortInvokerCheckTimer; - uint _currentWaypointNode; + uint _currentWaypointNodeId; bool _waypointReached; uint _waypointPauseTimer; bool _waypointPauseForced; @@ -82,7 +82,7 @@ namespace Game.AI if (path == null) return; - _currentWaypointNode = nodeId; + _currentWaypointNodeId = nodeId; _waypointPathEnded = false; _repeatWaypointPath = repeat; @@ -105,7 +105,7 @@ namespace Game.AI return null; WaypointPath path = Global.WaypointMgr.GetPath(entry); - if (path == null || path.nodes.Empty()) + if (path == null || path.Nodes.Empty()) { GetScript().SetPathId(0); return null; @@ -147,7 +147,7 @@ namespace Game.AI _waypointReached = false; AddEscortState(SmartEscortState.Paused); - GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, _currentWaypointNode, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, _currentWaypointNodeId, GetScript().GetPathId()); } public bool CanResumePath() @@ -196,7 +196,7 @@ namespace Game.AI me.GetMotionMaster().MoveIdle(); - GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, _currentWaypointNode, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, _currentWaypointNodeId, GetScript().GetPathId()); EndPath(fail); } @@ -262,7 +262,7 @@ namespace Game.AI return; uint pathid = GetScript().GetPathId(); - GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, _currentWaypointNode, pathid); + GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, _currentWaypointNodeId, pathid); if (_repeatWaypointPath) { @@ -278,7 +278,7 @@ namespace Game.AI public void ResumePath() { - GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, _currentWaypointNode, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, _currentWaypointNodeId, GetScript().GetPathId()); RemoveEscortState(SmartEscortState.Paused); @@ -378,9 +378,9 @@ namespace Game.AI return; } - _currentWaypointNode = nodeId; + _currentWaypointNodeId = nodeId; - GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, _currentWaypointNode, pathId); + GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, _currentWaypointNodeId, pathId); if (_waypointPauseTimer != 0 && !_waypointPauseForced) { @@ -391,7 +391,7 @@ namespace Game.AI else if (HasEscortState(SmartEscortState.Escorting) && me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint) { WaypointPath path = Global.WaypointMgr.GetPath(pathId); - if (path != null && _currentWaypointNode == path.nodes.Last()?.id) + if (path != null && _currentWaypointNodeId == path.Nodes.Last()?.Id) _waypointPathEnded = true; else SetRun(_run); @@ -568,8 +568,8 @@ namespace Game.AI if (formation == null || formation.GetLeader() == me || !formation.IsFormed()) { if (me.GetMotionMaster().GetCurrentMovementGeneratorType(MovementSlot.Default) != MovementGeneratorType.Waypoint) - if (me.GetWaypointPath() != 0) - me.GetMotionMaster().MovePath(me.GetWaypointPath(), true); + if (me.GetWaypointPathId() != 0) + me.GetMotionMaster().MovePath(me.GetWaypointPathId(), true); me.ResumeMovement(); } diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index b6240168b..17c853997 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -1645,7 +1645,7 @@ namespace Game.AI case SmartActions.WpStart: { WaypointPath path = Global.WaypointMgr.GetPath(e.Action.wpStart.pathID); - if (path == null || path.nodes.Empty()) + if (path == null || path.Nodes.Empty()) { Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} uses non-existent WaypointPath id {e.Action.wpStart.pathID}, skipped."); return false; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 47d71c208..78d308c6c 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -2027,17 +2027,17 @@ namespace Game.AI foreach (uint pathId in waypoints) { WaypointPath path = Global.WaypointMgr.GetPath(pathId); - if (path == null || path.nodes.Empty()) + if (path == null || path.Nodes.Empty()) continue; - foreach (var waypoint in path.nodes) + foreach (var waypoint in path.Nodes) { - float distToThisPath = creature.GetDistance(waypoint.x, waypoint.y, waypoint.z); - if (distToThisPath < distanceToClosest) + float distanceToThisNode = creature.GetDistance(waypoint.X, waypoint.Y, waypoint.Z); + if (distanceToThisNode < distanceToClosest) { - distanceToClosest = distToThisPath; + distanceToClosest = distanceToThisNode; closestPathId = pathId; - closestWaypointId = waypoint.id; + closestWaypointId = waypoint.Id; } } } diff --git a/Source/Game/Chat/Commands/ReloadCommand.cs b/Source/Game/Chat/Commands/ReloadCommand.cs index e183936ae..20e864297 100644 --- a/Source/Game/Chat/Commands/ReloadCommand.cs +++ b/Source/Game/Chat/Commands/ReloadCommand.cs @@ -915,16 +915,16 @@ namespace Game.Chat return true; } - [Command("waypoint_data", RBACPermissions.CommandReloadWaypointData, true)] + [Command("waypoint_path", RBACPermissions.CommandReloadWaypointPath, true)] static bool HandleReloadWpCommand(CommandHandler handler, StringArguments args) { if (args != null) - Log.outInfo(LogFilter.Server, "Re-Loading Waypoints data from 'waypoints_data'"); + Log.outInfo(LogFilter.Server, "Re-Loading Waypoints data from 'waypoint_path' and 'waypoint_path_node'"); - Global.WaypointMgr.Load(); + Global.WaypointMgr.LoadPaths(); if (args != null) - handler.SendGlobalGMSysMessage("DB Table 'waypoint_data' reloaded."); + handler.SendGlobalGMSysMessage("DB Tables 'waypoint_path' and 'waypoint_path_node' reloaded."); return true; } diff --git a/Source/Game/Chat/Commands/WPCommands.cs b/Source/Game/Chat/Commands/WPCommands.cs index 5467505dd..c7605007b 100644 --- a/Source/Game/Chat/Commands/WPCommands.cs +++ b/Source/Game/Chat/Commands/WPCommands.cs @@ -8,6 +8,7 @@ using Game.Entities; using Game.Maps; using System; using System.Collections.Generic; +using System.Linq; namespace Game.Chat.Commands { @@ -15,32 +16,31 @@ namespace Game.Chat.Commands class WPCommands { [Command("add", RBACPermissions.CommandWpAdd)] - static bool HandleWpAddCommand(CommandHandler handler, uint? optionalPathId) + static bool HandleWpAddCommand(CommandHandler handler, uint? optionalpathId) { - uint point = 0; Creature target = handler.GetSelectedCreature(); PreparedStatement stmt; uint pathId; - if (!optionalPathId.HasValue) + if (!optionalpathId.HasValue) { if (target != null) - pathId = target.GetWaypointPath(); + pathId = target.GetWaypointPathId(); else { - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_MAX_ID); + stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_MAX_PATHID); SQLResult result1 = DB.World.Query(stmt); - uint maxpathid = result1.Read(0); - pathId = maxpathid + 1; + uint maxpathId = result1.Read(0); + pathId = maxpathId + 1; handler.SendSysMessage("|cff00ff00New path started.|r"); } } else - pathId = optionalPathId.Value; + pathId = optionalpathId.Value; - // path_id . ID of the Path + // pathId . ID of the Path // point . number of the waypoint (if not 0) if (pathId == 0) @@ -49,39 +49,52 @@ namespace Game.Chat.Commands return true; } - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_MAX_POINT); + stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_MAX_NODEID); stmt.AddValue(0, pathId); SQLResult result = DB.World.Query(stmt); + uint nodeId = 0; if (result.IsEmpty()) - point = result.Read(0); + nodeId = result.Read(0); - Player player = handler.GetSession().GetPlayer(); + Player player = handler.GetPlayer(); - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.INS_WAYPOINT_DATA); + stmt = WorldDatabase.GetPreparedStatement(WorldStatements.INS_WAYPOINT_PATH_NODE); stmt.AddValue(0, pathId); - stmt.AddValue(1, point + 1); + stmt.AddValue(1, nodeId); stmt.AddValue(2, player.GetPositionX()); stmt.AddValue(3, player.GetPositionY()); stmt.AddValue(4, player.GetPositionZ()); stmt.AddValue(5, player.GetOrientation()); - DB.World.Execute(stmt); - handler.SendSysMessage("|cff00ff00PathID: |r|cff00ffff{0} |r|cff00ff00: Waypoint |r|cff00ffff{1}|r|cff00ff00 created.|r", pathId, point + 1); + if (target != null) + { + uint displayId = target.GetDisplayId(); + + WaypointPath path = Global.WaypointMgr.GetPath(pathId); + if (path == null) + return true; + + Global.WaypointMgr.DevisualizePath(player, path); + Global.WaypointMgr.ReloadPath(pathId); + Global.WaypointMgr.VisualizePath(player, path, displayId); + } + + handler.SendSysMessage("|cff00ff00pathId: |r|cff00ffff{pathId}|r|cff00ff00: Waypoint |r|cff00ffff{nodeId}|r|cff00ff00 created. "); return true; } [Command("load", RBACPermissions.CommandWpLoad)] - static bool HandleWpLoadCommand(CommandHandler handler, uint? optionalPathId) + static bool HandleWpLoadCommand(CommandHandler handler, uint? optionalpathId) { Creature target = handler.GetSelectedCreature(); - // Did player provide a path_id? - if (!optionalPathId.HasValue) + // Did player provide a pathId? + if (!optionalpathId.HasValue) return false; - uint pathId = optionalPathId.Value; + uint pathId = optionalpathId.Value; if (target == null) { @@ -143,19 +156,9 @@ namespace Game.Chat.Commands if (subCommand.IsEmpty()) return false; - // Check - // Remember: "show" must also be the name of a column! - if ((subCommand != "delay") && (subCommand != "action") && (subCommand != "action_chance") - && (subCommand != "move_flag") && (subCommand != "del") && (subCommand != "move")) - { - return false; - } - // Did user provide a GUID // or did the user select a creature? // . variable lowguid is filled with the GUID of the NPC - uint pathid; - uint point; Creature target = handler.GetSelectedCreature(); // User did select a visual waypoint? @@ -165,148 +168,50 @@ namespace Game.Chat.Commands return false; } - // Check the creature - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_WPGUID); - stmt.AddValue(0, target.GetSpawnId()); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) + WaypointPath path = Global.WaypointMgr.GetPathByVisualGUID(target.GetGUID()); + if (path == null) { - handler.SendSysMessage(CypherStrings.WaypointNotfoundsearch, target.GetGUID().ToString()); - // Select waypoint number from database - // Since we compare float values, we have to deal with - // some difficulties. - // Here we search for all waypoints that only differ in one from 1 thousand - // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html - string maxDiff = "0.01"; - - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_POS); - stmt.AddValue(0, target.GetPositionX()); - stmt.AddValue(1, maxDiff); - stmt.AddValue(2, target.GetPositionY()); - stmt.AddValue(3, maxDiff); - stmt.AddValue(4, target.GetPositionZ()); - stmt.AddValue(5, maxDiff); - result = DB.World.Query(stmt); - - if (result.IsEmpty()) - { - handler.SendSysMessage(CypherStrings.WaypointNotfounddbproblem, target.GetGUID().ToString()); - return true; - } + handler.SendSysMessage("|cff00ff00Path does not exist or target has no path|r"); + return true; } - do + WaypointNode node = Global.WaypointMgr.GetNodeByVisualGUID(target.GetGUID()); + if (node == null) { - pathid = result.Read(0); - point = result.Read(1); - } - while (result.NextRow()); - - // We have the waypoint number and the GUID of the "master npc" - // Text is enclosed in "<>", all other arguments not - - // Check for argument - if (subCommand != "del" && subCommand != "move") - { - handler.SendSysMessage(CypherStrings.WaypointArgumentreq, subCommand); - return false; + handler.SendSysMessage("|cff00ff00Path does not exist or target has no path|r"); + return true; } if (subCommand == "del") { - handler.SendSysMessage("|cff00ff00DEBUG: wp modify del, PathID: |r|cff00ffff{0}|r", pathid); + handler.SendSysMessage($"|cff00ff00DEBUG: .wp modify del, pathId: |r|cff00ffff{path.Id}|r, NodeId: |r|cff00ffff{node.Id}|r"); - if (Creature.DeleteFromDB(target.GetSpawnId())) - { + uint displayId = target.GetDisplayId(); - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.DEL_WAYPOINT_DATA); - stmt.AddValue(0, pathid); - stmt.AddValue(1, point); - DB.World.Execute(stmt); + Global.WaypointMgr.DevisualizePath(handler.GetPlayer(), path); + Global.WaypointMgr.DeleteNode(path, node); + Global.WaypointMgr.ReloadPath(path.Id); + Global.WaypointMgr.VisualizePath(handler.GetPlayer(), path, displayId); - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_DATA_POINT); - stmt.AddValue(0, pathid); - stmt.AddValue(1, point); - DB.World.Execute(stmt); - - handler.SendSysMessage(CypherStrings.WaypointRemoved); - return true; - } - else - { - handler.SendSysMessage(CypherStrings.WaypointNotremoved); - return false; - } - } // del - - if (subCommand == "move") + handler.SendSysMessage(CypherStrings.WaypointRemoved); + return true; + } + else if (subCommand == "move") { - handler.SendSysMessage("|cff00ff00DEBUG: wp move, PathID: |r|cff00ffff{0}|r", pathid); + handler.SendSysMessage("|cff00ff00DEBUG: .wp move, pathId: |r|cff00ffff%u|r, NodeId: |r|cff00ffff%u|r", path.Id, node.Id); - Player chr = handler.GetSession().GetPlayer(); - Map map = chr.GetMap(); - // What to do: - // Move the visual spawnpoint - // Respawn the owner of the waypoints - if (Creature.DeleteFromDB(target.GetSpawnId())) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); - return false; - } + uint displayId = target.GetDisplayId(); - // re-create - Creature creature = Creature.CreateCreature(1, map, chr.GetPosition()); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); - return false; - } - - PhasingHandler.InheritPhaseShift(creature, chr); - creature.SaveToDB(map.GetId(), new List() { map.GetDifficultyID() }); - - ulong dbGuid = creature.GetSpawnId(); - - // current "wpCreature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior - creature.CleanupsBeforeDelete(); - creature.Dispose(); - - // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); - return false; - } - - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_DATA_POSITION); - stmt.AddValue(0, chr.GetPositionX()); - stmt.AddValue(1, chr.GetPositionY()); - stmt.AddValue(2, chr.GetPositionZ()); - stmt.AddValue(3, chr.GetOrientation()); - stmt.AddValue(4, pathid); - stmt.AddValue(5, point); - DB.World.Execute(stmt); + Global.WaypointMgr.DevisualizePath(handler.GetPlayer(), path); + Global.WaypointMgr.MoveNode(path, node, handler.GetPlayer().GetPosition()); + Global.WaypointMgr.ReloadPath(path.Id); + Global.WaypointMgr.VisualizePath(handler.GetPlayer(), path, displayId); handler.SendSysMessage(CypherStrings.WaypointChanged); - return true; - } // move - - if (arg.IsEmpty()) - { - // show_str check for present in list of correct values, no sql injection possible - DB.World.Execute("UPDATE waypoint_data SET {0}=null WHERE id='{1}' AND point='{2}'", subCommand, pathid, point); // Query can't be a prepared statement - } - else - { - // show_str check for present in list of correct values, no sql injection possible - DB.World.Execute("UPDATE waypoint_data SET {0}='{1}' WHERE id='{2}' AND point='{3}'", subCommand, arg, pathid, point); // Query can't be a prepared statement } - handler.SendSysMessage(CypherStrings.WaypointChangedNo, subCommand); - return true; + return false; } [Command("reload", RBACPermissions.CommandWpReload)] @@ -321,328 +226,122 @@ namespace Game.Chat.Commands } [Command("show", RBACPermissions.CommandWpShow)] - static bool HandleWpShowCommand(CommandHandler handler, string subCommand, uint? optionalPathId) + static bool HandleWpShowCommand(CommandHandler handler, string subCommand, uint? optionalpathId) { // first arg: on, off, first, last if (subCommand.IsEmpty()) return false; + uint pathId; Creature target = handler.GetSelectedCreature(); - // Did player provide a PathID? - uint pathId; - - if (!optionalPathId.HasValue) + // Did player provide a pathId? + if (!optionalpathId.HasValue) { - // No PathID provided + // No pathId provided // . Player must have selected a creature if (target == null) { - handler.SendSysMessage(CypherStrings.SelectCreature); + handler.SendSysMessage(CypherStrings.SelectCreature); return false; } - pathId = target.GetWaypointPath(); + pathId = target.GetWaypointPathId(); } else { - // PathID provided + // pathId provided // Warn if player also selected a creature // . Creature selection is ignored <- if (target != null) handler.SendSysMessage(CypherStrings.WaypointCreatselected); - pathId = optionalPathId.Value; + pathId = optionalpathId.Value; } // Show info for the selected waypoint if (subCommand == "info") { - // Check if the user did specify a visual waypoint if (target == null || target.GetEntry() != 1) { - handler.SendSysMessage(CypherStrings.WaypointVpSelect); + handler.SendSysMessage(CypherStrings.WaypointVpSelect); return false; } - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_ALL_BY_WPGUID); - stmt.AddValue(0, target.GetSpawnId()); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) + WaypointPath path = Global.WaypointMgr.GetPathByVisualGUID(target.GetGUID()); + if (path == null) { - handler.SendSysMessage(CypherStrings.WaypointNotfounddbproblem, target.GetSpawnId()); + handler.SendSysMessage("|cff00ff00Path does not exist or target has no path|r"); + handler.SetSentErrorMessage(true); + return false; + } + + WaypointNode node = Global.WaypointMgr.GetNodeByVisualGUID(target.GetGUID()); + if (node == null) + { + handler.SendSysMessage("|cff00ff00Path does not exist or target has no path|r"); + handler.SetSentErrorMessage(true); + return false; + + } + + handler.SendSysMessage("|cff00ffffDEBUG: .wp show info:|r"); + handler.SendSysMessage($"|cff00ff00Show info: Path Id: |r|cff00ffff{path.Id}|r"); + handler.SendSysMessage($"|cff00ff00Show info: Path MoveType: |r|cff00ffff{(uint)path.MoveType}|r"); + handler.SendSysMessage($"|cff00ff00Show info: Path Flags: |r|cff00ffff{(uint)path.Flags}|r"); + handler.SendSysMessage($"|cff00ff00Show info: Node Id: |r|cff00ffff{node.Id}|r"); + handler.SendSysMessage($"|cff00ff00Show info: Node Delay: |r|cff00ffff{node.Id}|r"); + + return true; + } + else if (subCommand == "on") + { + WaypointPath path = Global.WaypointMgr.GetPath(pathId); + if (path == null) + { + handler.SendSysMessage($"|cff00ff00Path does not exist: id {pathId}|r"); return true; } - handler.SendSysMessage("|cff00ffffDEBUG: wp show info:|r"); - do + if (path.Nodes.Empty()) { - pathId = result.Read(0); - uint point = result.Read(1); - uint delay = result.Read(2); - uint flag = result.Read(3); - - handler.SendSysMessage("|cff00ff00Show info: for current point: |r|cff00ffff{0}|r|cff00ff00, Path ID: |r|cff00ffff{1}|r", point, pathId); - handler.SendSysMessage("|cff00ff00Show info: delay: |r|cff00ffff{0}|r", delay); - handler.SendSysMessage("|cff00ff00Show info: Move flag: |r|cff00ffff{0}|r", flag); - } - while (result.NextRow()); - - return true; - } - - if (subCommand == "on") - { - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_BY_ID); - stmt.AddValue(0, pathId); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) - { - handler.SendSysMessage("|cffff33ffPath no found.|r"); - return false; - } - - handler.SendSysMessage("|cff00ff00DEBUG: wp on, PathID: |cff00ffff{0}|r", pathId); - - // Delete all visuals for this NPC - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_WPGUID_BY_ID); - stmt.AddValue(0, pathId); - SQLResult result2 = DB.World.Query(stmt); - - if (!result2.IsEmpty()) - { - bool hasError = false; - do - { - ulong wpguid = result2.Read(0); - - if (Creature.DeleteFromDB(wpguid)) - { - handler.SendSysMessage(CypherStrings.WaypointNotremoved, wpguid); - hasError = true; - } - - } - while (result2.NextRow()); - - if (hasError) - { - handler.SendSysMessage(CypherStrings.WaypointToofar1); - handler.SendSysMessage(CypherStrings.WaypointToofar2); - handler.SendSysMessage(CypherStrings.WaypointToofar3); - } - } - - do - { - uint point = result.Read(0); - float x = result.Read(1); - float y = result.Read(2); - float z = result.Read(3); - float o = result.Read(4); - - uint id = 1; - - Player chr = handler.GetSession().GetPlayer(); - Map map = chr.GetMap(); - - Creature creature = Creature.CreateCreature(id, map, new Position(x, y, z, o)); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id); - return false; - } - - PhasingHandler.InheritPhaseShift(creature, chr); - creature.SaveToDB(map.GetId(), new List() { map.GetDifficultyID() }); - - ulong dbGuid = creature.GetSpawnId(); - - // current "wpCreature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior - creature.CleanupsBeforeDelete(); - creature.Dispose(); - - // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id); - return false; - } - - if (target != null) - { - creature.SetDisplayId(target.GetDisplayId()); - creature.SetObjectScale(0.5f); - creature.SetLevel(Math.Min(point, SharedConst.StrongMaxLevel)); - } - - // Set "wpguid" column to the visual waypoint - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_DATA_WPGUID); - stmt.AddValue(0, creature.GetSpawnId()); - stmt.AddValue(1, pathId); - stmt.AddValue(2, point); - DB.World.Execute(stmt); - } - while (result.NextRow()); - - handler.SendSysMessage("|cff00ff00Showing the current creature's path.|r"); - return true; - } - - if (subCommand == "first") - { - handler.SendSysMessage("|cff00ff00DEBUG: wp first, pathid: {0}|r", pathId); - - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_FIRST_BY_ID); - stmt.AddValue(0, pathId); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) - { - handler.SendSysMessage(CypherStrings.WaypointNotfound, pathId); - return false; - } - - float x = result.Read(0); - float y = result.Read(1); - float z = result.Read(2); - float o = result.Read(3); - - Player chr = handler.GetSession().GetPlayer(); - Map map = chr.GetMap(); - - Creature creature = Creature.CreateCreature(1, map, new Position(x, y, z, 0)); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); - return false; - } - - PhasingHandler.InheritPhaseShift(creature, chr); - creature.SaveToDB(map.GetId(), new List() { map.GetDifficultyID() }); - - ulong dbGuid = creature.GetSpawnId(); - - // current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior - creature.CleanupsBeforeDelete(); - creature.Dispose(); - - creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); - return false; + handler.SendSysMessage($"|cff00ff00Path does not have any nodes: id {pathId}|r"); + return true; } + uint? displayId = null; if (target != null) + displayId = target.GetDisplayId(); + + Global.WaypointMgr.VisualizePath(handler.GetPlayer(), path, displayId); + + ObjectGuid guid = Global.WaypointMgr.GetVisualGUIDByNode(path.Id, path.Nodes.First().Id); + if (!guid.IsEmpty()) { - creature.SetDisplayId(target.GetDisplayId()); - creature.SetObjectScale(0.5f); + handler.SendSysMessage($"|cff00ff00Path with id {pathId} is already showing.|r"); + return true; } + handler.SendSysMessage($"|cff00ff00Showing path with id {pathId}.|r"); return true; } - - if (subCommand == "last") + else if (subCommand == "off") { - handler.SendSysMessage("|cff00ff00DEBUG: wp last, PathID: |r|cff00ffff{0}|r", pathId); - - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_POS_LAST_BY_ID); - stmt.AddValue(0, pathId); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) + WaypointPath path = Global.WaypointMgr.GetPath(pathId); + if (path == null) { - handler.SendSysMessage(CypherStrings.WaypointNotfoundlast, pathId); - return false; + handler.SendSysMessage($"|cff00ff00Path does not exist: id {pathId}|r"); + return true; } - float x = result.Read(0); - float y = result.Read(1); - float z = result.Read(2); - float o = result.Read(3); - - Player chr = handler.GetSession().GetPlayer(); - Map map = chr.GetMap(); - Position pos = new(x, y, z, o); - - Creature creature = Creature.CreateCreature(1, map, pos); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1); - return false; - } - - PhasingHandler.InheritPhaseShift(creature, chr); - creature.SaveToDB(map.GetId(), new List() { map.GetDifficultyID() }); - - ulong dbGuid = creature.GetSpawnId(); - - // current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior - creature.CleanupsBeforeDelete(); - creature.Dispose(); - - creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true); - if (creature == null) - { - handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1); - return false; - } - - if (target != null) - { - creature.SetDisplayId(target.GetDisplayId()); - creature.SetObjectScale(0.5f); - } - - return true; - } - - if (subCommand == "off") - { - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_CREATURE_BY_ID); - stmt.AddValue(0, 1); - SQLResult result = DB.World.Query(stmt); - - if (result.IsEmpty()) - { - handler.SendSysMessage(CypherStrings.WaypointVpNotfound); - return false; - } - bool hasError = false; - do - { - ulong lowguid = result.Read(0); - - if (Creature.DeleteFromDB(lowguid)) - { - handler.SendSysMessage(CypherStrings.WaypointNotremoved, lowguid); - hasError = true; - } - } - while (result.NextRow()); - // set "wpguid" column to "empty" - no visual waypoint spawned - stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_DATA_ALL_WPGUID); - - DB.World.Execute(stmt); - //DB.World.PExecute("UPDATE creature_movement SET wpguid = '0' WHERE wpguid <> '0'"); - - if (hasError) - { - handler.SendSysMessage(CypherStrings.WaypointToofar1); - handler.SendSysMessage(CypherStrings.WaypointToofar2); - handler.SendSysMessage(CypherStrings.WaypointToofar3); - } + Global.WaypointMgr.DevisualizePath(handler.GetPlayer(), path); handler.SendSysMessage(CypherStrings.WaypointVpAllremoved); return true; } - handler.SendSysMessage("|cffff33ffDEBUG: wpshow - no valid command found|r"); + handler.SendSysMessage("|cffff33ffDEBUG: .wp show - no valid command found|r"); return true; } @@ -664,7 +363,7 @@ namespace Game.Chat.Commands } CreatureAddon addon = Global.ObjectMgr.GetCreatureAddon(guidLow); - if (addon == null || addon.path_id == 0) + if (addon == null || addon.PathId == 0) { handler.SendSysMessage("|cffff33ffTarget does not have a loaded path.|r"); return true; diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index d14383ab9..714b32de7 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1118,7 +1118,7 @@ namespace Game.Entities public override string GetDebugInfo() { - return $"{base.GetDebugInfo()}\nAIName: {GetAIName()} ScriptName: {GetScriptName()} WaypointPath: {GetWaypointPath()} SpawnId: {GetSpawnId()}"; + return $"{base.GetDebugInfo()}\nAIName: {GetAIName()} ScriptName: {GetScriptName()} WaypointPath: {GetWaypointPathId()} SpawnId: {GetSpawnId()}"; } public override void ExitVehicle(Position exitPosition = null) @@ -2525,8 +2525,8 @@ namespace Game.Entities SetVisibilityDistanceOverride(creatureAddon.visibilityDistanceType); //Load Path - if (creatureAddon.path_id != 0) - _waypointPathId = creatureAddon.path_id; + if (creatureAddon.PathId != 0) + _waypointPathId = creatureAddon.PathId; if (creatureAddon.auras != null) { @@ -3509,7 +3509,7 @@ namespace Game.Entities public void GetTransportHomePosition(out float x, out float y, out float z, out float ori) { m_transportHomePosition.GetPosition(out x, out y, out z, out ori); } public Position GetTransportHomePosition() { return m_transportHomePosition; } - public uint GetWaypointPath() { return _waypointPathId; } + public uint GetWaypointPathId() { return _waypointPathId; } public void LoadPath(uint pathid) { _waypointPathId = pathid; } public (uint nodeId, uint pathId) GetCurrentWaypointInfo() { return _currentWaypointNodeInfo; } diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index b31cea5a8..b0357ede1 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -347,7 +347,7 @@ namespace Game.Entities public class CreatureAddon { - public uint path_id; + public uint PathId; public uint mount; public byte standState; public byte animTier; diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index e336511b8..80b18ea76 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -2081,8 +2081,8 @@ namespace Game public void LoadCreatureTemplateAddons() { var time = Time.GetMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - SQLResult result = DB.World.Query("SELECT entry, path_id, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + SQLResult result = DB.World.Query("SELECT entry, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); if (result.IsEmpty()) { @@ -2101,7 +2101,7 @@ namespace Game } CreatureAddon creatureAddon = new(); - creatureAddon.path_id = result.Read(1); + creatureAddon.PathId = result.Read(1); creatureAddon.mount = result.Read(2); creatureAddon.standState = result.Read(3); creatureAddon.animTier = result.Read(4); @@ -2214,8 +2214,8 @@ namespace Game public void LoadCreatureAddons() { var time = Time.GetMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - SQLResult result = DB.World.Query("SELECT guid, path_id, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + SQLResult result = DB.World.Query("SELECT guid, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); if (result.IsEmpty()) { @@ -2236,8 +2236,8 @@ namespace Game CreatureAddon creatureAddon = new(); - creatureAddon.path_id = result.Read(1); - if (creData.movementType == (byte)MovementGeneratorType.Waypoint && creatureAddon.path_id == 0) + creatureAddon.PathId = result.Read(1); + if (creData.movementType == (byte)MovementGeneratorType.Waypoint && creatureAddon.PathId == 0) { creData.movementType = (byte)MovementGeneratorType.Idle; Log.outError(LogFilter.Sql, $"Creature (GUID {guid}) has movement type set to WAYPOINTMOTIONTYPE but no path assigned"); diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index 27497ed1b..c46696d5c 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -113,15 +113,15 @@ namespace Game.Movement x = y = z = 0; // prevent a crash at empty waypoint path. - if (_path == null || _path.nodes.Empty()) + if (_path == null || _path.Nodes.Empty()) return false; - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator::GetResetPosition: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); - WaypointNode waypoint = _path.nodes.ElementAt(_currentNode); + Cypher.Assert(_currentNode < _path.Nodes.Count, $"WaypointMovementGenerator::GetResetPosition: tried to reference a node id ({_currentNode}) which is not included in path ({_path.Id})"); + WaypointNode waypoint = _path.Nodes.ElementAt(_currentNode); - x = waypoint.x; - y = waypoint.y; - z = waypoint.z; + x = waypoint.X; + y = waypoint.Y; + z = waypoint.Z; return true; } @@ -132,7 +132,7 @@ namespace Game.Movement if (_loadedFromDB) { if (_pathId == 0) - _pathId = owner.GetWaypointPath(); + _pathId = owner.GetWaypointPathId(); _path = Global.WaypointMgr.GetPath(_pathId); } @@ -143,6 +143,11 @@ namespace Game.Movement return; } + _followPathBackwardsFromEndToStart = _path.Flags.HasFlag(WaypointPathFlags.FollowPathBackwardsFromEndToStart); + + if (_path.Nodes.Count == 1) + _repeating = false; + owner.StopMoving(); _nextMoveTime.Reset(1000); @@ -163,7 +168,7 @@ namespace Game.Movement if (owner == null || !owner.IsAlive()) return true; - if (HasFlag(MovementGeneratorFlags.Finalized | MovementGeneratorFlags.Paused) || _path == null || _path.nodes.Empty()) + if (HasFlag(MovementGeneratorFlags.Finalized | MovementGeneratorFlags.Paused) || _path == null || _path.Nodes.Empty()) return true; if (_duration != null) @@ -267,28 +272,32 @@ namespace Game.Movement } } - void MovementInform(Creature owner) + public void MovementInform(Creature owner) { + WaypointNode waypoint = _path.Nodes.ElementAt(_currentNode); CreatureAI ai = owner.GetAI(); if (ai != null) - ai.MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode); + { + ai.MovementInform(MovementGeneratorType.Waypoint, waypoint.Id); + ai.WaypointReached(waypoint.Id, _path.Id); + } } void OnArrived(Creature owner) { - if (_path == null || _path.nodes.Empty()) + if (_path == null || _path.Nodes.Empty()) return; - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.OnArrived: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); - WaypointNode waypoint = _path.nodes.ElementAt(_currentNode); - if (waypoint.delay != 0) + Cypher.Assert(_currentNode < _path.Nodes.Count, $"WaypointMovementGenerator.OnArrived: tried to reference a node id ({_currentNode}) which is not included in path ({_path.Id})"); + WaypointNode waypoint = _path.Nodes.ElementAt(_currentNode); + if (waypoint.Delay != 0) { owner.ClearUnitState(UnitState.RoamingMove); - _nextMoveTime.Reset(waypoint.delay); + _nextMoveTime.Reset(waypoint.Delay); } if (_waitTimeRangeAtPathEnd.HasValue && _followPathBackwardsFromEndToStart - && ((_isReturningToStart && _currentNode == 0) || (!_isReturningToStart && _currentNode == _path.nodes.Count - 1))) + && ((_isReturningToStart && _currentNode == 0) || (!_isReturningToStart && _currentNode == _path.Nodes.Count - 1))) { owner.ClearUnitState(UnitState.RoamingMove); TimeSpan waitTime = RandomHelper.RandTime(_waitTimeRangeAtPathEnd.Value.min, _waitTimeRangeAtPathEnd.Value.max); @@ -301,21 +310,15 @@ namespace Game.Movement _nextMoveTime.Reset(waitTime); } - // inform AI - CreatureAI ai = owner.GetAI(); - if (ai != null) - { - ai.MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode); - ai.WaypointReached(waypoint.id, _path.id); - } + MovementInform(owner); - owner.UpdateCurrentWaypointInfo(waypoint.id, _path.id); + owner.UpdateCurrentWaypointInfo(waypoint.Id, _path.Id); } void StartMove(Creature owner, bool relaunch = false) { // sanity checks - if (owner == null || !owner.IsAlive() || HasFlag(MovementGeneratorFlags.Finalized) || _path == null || _path.nodes.Empty() || (relaunch && (HasFlag(MovementGeneratorFlags.InformEnabled) || !HasFlag(MovementGeneratorFlags.Initialized)))) + if (owner == null || !owner.IsAlive() || HasFlag(MovementGeneratorFlags.Finalized) || _path == null || _path.Nodes.Empty() || (relaunch && (HasFlag(MovementGeneratorFlags.InformEnabled) || !HasFlag(MovementGeneratorFlags.Initialized)))) return; if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting() || (owner.IsFormationLeader() && !owner.IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation @@ -330,18 +333,18 @@ namespace Game.Movement { if (ComputeNextNode()) { - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); + Cypher.Assert(_currentNode < _path.Nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.Id})"); // inform AI CreatureAI ai = owner.GetAI(); if (ai != null) - ai.WaypointStarted(_path.nodes[_currentNode].id, _path.id); + ai.WaypointStarted(_path.Nodes[_currentNode].Id, _path.Id); } else { - WaypointNode currentWaypoint = _path.nodes[_currentNode]; - float x = currentWaypoint.x; - float y = currentWaypoint.y; - float z = currentWaypoint.z; + WaypointNode currentWaypoint = _path.Nodes[_currentNode]; + float x = currentWaypoint.X; + float y = currentWaypoint.Y; + float z = currentWaypoint.Z; float o = owner.GetOrientation(); if (!transportPath) @@ -365,7 +368,7 @@ namespace Game.Movement // inform AI CreatureAI ai = owner.GetAI(); if (ai != null) - ai.WaypointPathEnded(currentWaypoint.id, _path.id); + ai.WaypointPathEnded(currentWaypoint.Id, _path.Id); return; } } @@ -376,11 +379,11 @@ namespace Game.Movement // inform AI CreatureAI ai = owner.GetAI(); if (ai != null) - ai.WaypointStarted(_path.nodes[_currentNode].id, _path.id); + ai.WaypointStarted(_path.Nodes[_currentNode].Id, _path.Id); } - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); - WaypointNode waypoint = _path.nodes[_currentNode]; + Cypher.Assert(_currentNode < _path.Nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.Id})"); + WaypointNode waypoint = _path.Nodes[_currentNode]; RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.InformEnabled | MovementGeneratorFlags.TimedPaused); @@ -394,12 +397,12 @@ namespace Game.Movement //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call //! but formationDest contains global coordinates - init.MoveTo(waypoint.x, waypoint.y, waypoint.z, _generatePath); + init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z, _generatePath); - if (waypoint.orientation.HasValue && (waypoint.delay > 0 || _currentNode == _path.nodes.Count - 1)) - init.SetFacing(waypoint.orientation.Value); + if (waypoint.Orientation.HasValue && (waypoint.Delay > 0 || _currentNode == _path.Nodes.Count - 1)) + init.SetFacing(waypoint.Orientation.Value); - switch (waypoint.moveType) + switch (_path.MoveType) { case WaypointMoveType.Land: init.SetAnimation(AnimTier.Ground); @@ -440,16 +443,16 @@ namespace Game.Movement bool ComputeNextNode() { - if ((_currentNode == _path.nodes.Count - 1) && !_repeating) + if ((_currentNode == _path.Nodes.Count - 1) && !_repeating) return false; - if (!_followPathBackwardsFromEndToStart || _path.nodes.Count < 2) - _currentNode = (_currentNode + 1) % _path.nodes.Count; + if (!_followPathBackwardsFromEndToStart || _path.Nodes.Count < 2) + _currentNode = (_currentNode + 1) % _path.Nodes.Count; else { if (!_isReturningToStart) { - if (++_currentNode >= _path.nodes.Count) + if (++_currentNode >= _path.Nodes.Count) { _currentNode -= 2; _isReturningToStart = true; diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 89338459f..83c497787 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -1035,7 +1035,7 @@ namespace Game.Movement public void MovePath(WaypointPath path, bool repeatable, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool followPathBackwardsFromEndToStart = false, bool generatePath = true) { - Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePath: '{_owner.GetGUID()}', starts moving over path Id: {path.id} (repeatable: {repeatable})"); + Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePath: '{_owner.GetGUID()}', starts moving over path Id: {path.Id} (repeatable: {repeatable})"); Add(new WaypointMovementGenerator(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath), MovementSlot.Default); } diff --git a/Source/Game/Movement/WaypointManager.cs b/Source/Game/Movement/WaypointManager.cs index c7332abf1..545820034 100644 --- a/Source/Game/Movement/WaypointManager.cs +++ b/Source/Game/Movement/WaypointManager.cs @@ -2,8 +2,11 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Database; +using Game.Entities; using Game.Maps; +using System; using System.Collections.Generic; +using System.Linq; namespace Game { @@ -11,16 +14,24 @@ namespace Game { WaypointManager() { } - public void Load() + public void LoadPaths() { + _LoadPaths(); + _LoadPathNodes(); + DoPostLoadingChecks(); + } + + void _LoadPaths() + { + _pathStorage.Clear(); + var oldMSTime = Time.GetMSTime(); - // 0 1 2 3 4 5 6 7 - SQLResult result = DB.World.Query("SELECT id, point, position_x, position_y, position_z, orientation, move_type, delay FROM waypoint_data ORDER BY id, point"); - + // 0 1 2 + SQLResult result = DB.World.Query("SELECT PathId, MoveType, Flags FROM waypoint_path"); if (result.IsEmpty()) { - Log.outInfo(LogFilter.ServerLoading, "Loaded 0 waypoints. DB table `waypoint_data` is empty!"); + Log.outInfo(LogFilter.ServerLoading, "Loaded 0 waypoint paths. DB table `waypoint_path` is empty!"); return; } @@ -28,143 +39,308 @@ namespace Game do { - uint pathId = result.Read(0); - - float x = result.Read(2); - float y = result.Read(3); - float z = result.Read(4); - float? o = null; - if (!result.IsNull(5)) - o = result.Read(5); - - GridDefines.NormalizeMapCoord(ref x); - GridDefines.NormalizeMapCoord(ref y); - - WaypointNode waypoint = new(); - waypoint.id = result.Read(1); - waypoint.x = x; - waypoint.y = y; - waypoint.z = z; - waypoint.orientation = o; - waypoint.moveType = (WaypointMoveType)result.Read(6); - - if (waypoint.moveType >= WaypointMoveType.Max) - { - Log.outError(LogFilter.Sql, $"Waypoint {waypoint.id} in waypoint_data has invalid move_type, ignoring"); - continue; - } - - waypoint.delay = result.Read(7); - - if (!_waypointStore.ContainsKey(pathId)) - _waypointStore[pathId] = new WaypointPath(); - - WaypointPath path = _waypointStore[pathId]; - path.id = pathId; - path.nodes.Add(waypoint); - + LoadPathFromDB(result.GetFields()); ++count; } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} waypoints in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); + Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} waypoint paths in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } - public void ReloadPath(uint id) + void _LoadPathNodes() { - _waypointStore.Remove(id); - - PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_ID); - stmt.AddValue(0, id); - SQLResult result = DB.World.Query(stmt); + uint oldMSTime = Time.GetMSTime(); + // 0 1 2 3 4 5 6 + SQLResult result = DB.World.Query("SELECT PathId, NodeId, PositionX, PositionY, PositionZ, Orientation, Delay FROM waypoint_path_node ORDER BY PathId, NodeId"); if (result.IsEmpty()) + { + Log.outInfo(LogFilter.ServerLoading, "Loaded 0 waypoint path nodes. DB table `waypoint_path_node` is empty!"); return; + } + + uint count = 0; - List values = new(); do { - float x = result.Read(1); - float y = result.Read(2); - float z = result.Read(3); - float? o = null; - if (!result.IsNull(4)) - o = result.Read(4); - - GridDefines.NormalizeMapCoord(ref x); - GridDefines.NormalizeMapCoord(ref y); - - WaypointNode waypoint = new(); - waypoint.id = result.Read(0); - waypoint.x = x; - waypoint.y = y; - waypoint.z = z; - waypoint.orientation = o; - waypoint.moveType = (WaypointMoveType)result.Read(5); - - if (waypoint.moveType >= WaypointMoveType.Max) - { - Log.outError(LogFilter.Sql, $"Waypoint {waypoint.id} in waypoint_data has invalid move_type, ignoring"); - continue; - } - - waypoint.delay = result.Read(6); - - values.Add(waypoint); + LoadPathNodesFromDB(result.GetFields()); + ++count; } while (result.NextRow()); - _waypointStore[id] = new WaypointPath(id, values); + Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} waypoint path nodes in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); + DoPostLoadingChecks(); } - public WaypointPath GetPath(uint id) + void LoadPathFromDB(SQLFields fields) { - return _waypointStore.LookupByKey(id); + uint pathId = fields.Read(0); + + WaypointPath path = new(); + path.Id = pathId; + path.MoveType = (WaypointMoveType)fields.Read(1); + + if (path.MoveType >= WaypointMoveType.Max) + { + Log.outError(LogFilter.Sql, $"PathId {pathId} in `waypoint_path` has invalid MoveType {path.MoveType}, ignoring"); + return; + } + path.Flags = (WaypointPathFlags)fields.Read(2); + path.Nodes.Clear(); + + _pathStorage.Add(pathId, path); } - Dictionary _waypointStore = new(); + void LoadPathNodesFromDB(SQLFields fields) + { + uint pathId = fields.Read(0); + + if (!_pathStorage.ContainsKey(pathId)) + { + Log.outError(LogFilter.Sql, $"PathId {pathId} in `waypoint_path_node` does not exist in `waypoint_path`, ignoring"); + return; + } + + float x = fields.Read(2); + float y = fields.Read(3); + float z = fields.Read(4); + float? o = null; + if (!fields.IsNull(5)) + o = fields.Read(5); + + GridDefines.NormalizeMapCoord(ref x); + GridDefines.NormalizeMapCoord(ref y); + + WaypointNode waypoint = new(fields.Read(1), x, y, z, o, fields.Read(6)); + _pathStorage[pathId].Nodes.Add(waypoint); + } + + void DoPostLoadingChecks() + { + foreach (var path in _pathStorage) + { + WaypointPath pathInfo = path.Value; + if (pathInfo.Nodes.Empty()) + Log.outError(LogFilter.Sql, $"PathId {pathInfo.Id} in `waypoint_path` has no assigned nodes in `waypoint_path_node`"); + + if (pathInfo.Flags.HasFlag(WaypointPathFlags.FollowPathBackwardsFromEndToStart) && pathInfo.Nodes.Count < 2) + Log.outError(LogFilter.Sql, $"PathId {pathInfo.Id} in `waypoint_path` has FollowPathBackwardsFromEndToStart set, but only {pathInfo.Nodes.Count} nodes, requires {2}"); + } + } + + public void ReloadPath(uint pathId) + { + // waypoint_path + PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_PATH_BY_PATHID); + stmt.AddValue(0, pathId); + SQLResult result = DB.World.Query(stmt); + if (result.IsEmpty()) + { + Log.outError(LogFilter.Sql, $"PathId {pathId} in `waypoint_path` not found, ignoring"); + return; + } + + do + { + LoadPathFromDB(result.GetFields()); + } while (result.NextRow()); + + // waypoint_path_data + stmt = WorldDatabase.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_PATH_NODE_BY_PATHID); + stmt.AddValue(0, pathId); + + result = DB.World.Query(stmt); + if (result.IsEmpty()) + { + Log.outError(LogFilter.Sql, $"PathId {pathId} in `waypoint_path_node` not found, ignoring"); + return; + } + + do + { + LoadPathNodesFromDB(result.GetFields()); + } while (result.NextRow()); + } + + public void VisualizePath(Unit owner, WaypointPath path, uint? displayId) + { + foreach (WaypointNode node in path.Nodes) + { + var pathNodePair = (path.Id, node.Id); + if (!_nodeToVisualWaypointGUIDsMap.ContainsKey(pathNodePair)) + continue; + + TempSummon summon = owner.SummonCreature(1, node.X, node.Y, node.Z, node.Orientation.HasValue ? node.Orientation.Value : 0.0f); + if (summon == null) + continue; + + if (displayId.HasValue) + { + summon.SetDisplayId(displayId.Value, true); + summon.SetObjectScale(0.5f); + } + _nodeToVisualWaypointGUIDsMap[pathNodePair] = summon.GetGUID(); + _visualWaypointGUIDToNodeMap[summon.GetGUID()] = (path, node); + } + } + + public void DevisualizePath(Unit owner, WaypointPath path) + { + foreach (WaypointNode node in path.Nodes) + { + var pathNodePair = (path.Id, node.Id); + if (!_nodeToVisualWaypointGUIDsMap.TryGetValue(pathNodePair, out ObjectGuid guid)) + continue; + + Creature creature = ObjectAccessor.GetCreature(owner, guid); + if (creature == null) + continue; + + _visualWaypointGUIDToNodeMap.Remove(guid); + _nodeToVisualWaypointGUIDsMap.Remove(pathNodePair); + + creature.DespawnOrUnsummon(); + } + } + + public void MoveNode(WaypointPath path, WaypointNode node, Position pos) + { + PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_PATH_NODE_POSITION); + stmt.AddValue(0, pos.GetPositionX()); + stmt.AddValue(1, pos.GetPositionY()); + stmt.AddValue(2, pos.GetPositionZ()); + stmt.AddValue(3, pos.GetOrientation()); + stmt.AddValue(4, path.Id); + stmt.AddValue(5, node.Id); + DB.World.Execute(stmt); + } + + public void DeleteNode(WaypointPath path, WaypointNode node) + { + PreparedStatement stmt = WorldDatabase.GetPreparedStatement(WorldStatements.DEL_WAYPOINT_PATH_NODE); + stmt.AddValue(0, path.Id); + stmt.AddValue(1, node.Id); + DB.World.Execute(stmt); + + stmt = WorldDatabase.GetPreparedStatement(WorldStatements.UPD_WAYPOINT_PATH_NODE); + stmt.AddValue(0, path.Id); + stmt.AddValue(1, node.Id); + DB.World.Execute(stmt); + } + + void DeleteNode(uint pathId, uint nodeId) + { + WaypointPath path = GetPath(pathId); + if (path == null) + return; + + WaypointNode node = GetNode(path, nodeId); + if (node == null) + return; + + DeleteNode(path, node); + } + + public WaypointPath GetPath(uint pathId) + { + return _pathStorage.LookupByKey(pathId); + } + + public WaypointNode GetNode(WaypointPath path, uint nodeId) + { + return path.Nodes.FirstOrDefault(node => node.Id == nodeId); ; + } + + public WaypointNode GetNode(uint pathId, uint nodeId) + { + WaypointPath path = GetPath(pathId); + if (path == null) + return null; + + return GetNode(path.Id, nodeId); + } + + public WaypointPath GetPathByVisualGUID(ObjectGuid guid) + { + if (!_visualWaypointGUIDToNodeMap.TryGetValue(guid, out var pair)) + return null; + + return pair.Item1; + } + + public WaypointNode GetNodeByVisualGUID(ObjectGuid guid) + { + if (!_visualWaypointGUIDToNodeMap.TryGetValue(guid, out var pair)) + return null; + + return pair.Item2; + } + + public ObjectGuid GetVisualGUIDByNode(uint pathId, uint nodeId) + { + if (!_nodeToVisualWaypointGUIDsMap.TryGetValue((pathId, nodeId), out var guid)) + return ObjectGuid.Empty; + + return guid; + } + + Dictionary _pathStorage = new(); + + Dictionary<(uint /*pathId*/, uint /*nodeId*/), ObjectGuid> _nodeToVisualWaypointGUIDsMap = new(); + Dictionary _visualWaypointGUIDToNodeMap = new(); } public class WaypointNode { - public WaypointNode() { moveType = WaypointMoveType.Run; } - public WaypointNode(uint _id, float _x, float _y, float _z, float? _orientation = null, uint _delay = 0) + public WaypointNode() { MoveType = WaypointMoveType.Run; } + public WaypointNode(uint id, float x, float y, float z, float? orientation = null, uint delay = 0) { - id = _id; - x = _x; - y = _y; - z = _z; - orientation = _orientation; - delay = _delay; - moveType = WaypointMoveType.Walk; + Id = id; + X = x; + Y = y; + Z = z; + Orientation = orientation; + Delay = delay; + MoveType = WaypointMoveType.Walk; } - public uint id; - public float x, y, z; - public float? orientation; - public uint delay; - public WaypointMoveType moveType; + public uint Id; + public float X; + public float Y; + public float Z; + public float? Orientation; + public uint Delay; + public WaypointMoveType MoveType; } public class WaypointPath { public WaypointPath() { } - public WaypointPath(uint _id, List _nodes) + public WaypointPath(uint id, List nodes) { - id = _id; - nodes = _nodes; + Id = id; + Nodes = nodes; } - public List nodes = new(); - public uint id; + public List Nodes = new(); + public uint Id; + public WaypointMoveType MoveType; + public WaypointPathFlags Flags = WaypointPathFlags.None; } public enum WaypointMoveType { - Walk, - Run, - Land, - Takeoff, + Walk = 0, + Run = 1, + Land = 2, + Takeoff = 3, Max } + + [Flags] + public enum WaypointPathFlags + { + None = 0x00, + FollowPathBackwardsFromEndToStart = 0x01, + } } diff --git a/Source/Game/World/WorldManager.cs b/Source/Game/World/WorldManager.cs index 20739dc38..236d2fed7 100644 --- a/Source/Game/World/WorldManager.cs +++ b/Source/Game/World/WorldManager.cs @@ -898,7 +898,7 @@ namespace Game Global.ObjectMgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate Log.outInfo(LogFilter.ServerLoading, "Loading Waypoints..."); - Global.WaypointMgr.Load(); + Global.WaypointMgr.LoadPaths(); Log.outInfo(LogFilter.ServerLoading, "Loading Creature Formations..."); FormationMgr.LoadCreatureFormations();