/// <summary> /// Links scenes objects together by cardinal direction /// </summary> /// <param name="scene"></param> public static void PopulateSceneConnections(CachedScene scene) { scene.ConnectedScenes.Clear(); scene.ConnectedSceneGuids.Clear(); scene.WestConnection = Scenes.FirstOrDefault(s => s.SceneHash != scene.SceneHash && s.EastEdgeCenter == scene.WestEdgeCenter && s.ExitCodes.Contains(Direction.East)); scene.EastConnection = Scenes.FirstOrDefault(s => s.SceneHash != scene.SceneHash && s.WestEdgeCenter == scene.EastEdgeCenter && s.ExitCodes.Contains(Direction.West)); scene.NorthConnection = Scenes.FirstOrDefault(s => s.SceneHash != scene.SceneHash && s.SouthEdgeCenter == scene.NorthEdgeCenter && s.ExitCodes.Contains(Direction.South)); scene.SouthConnection = Scenes.FirstOrDefault(s => s.SceneHash != scene.SceneHash && s.NorthEdgeCenter == scene.SouthEdgeCenter && s.ExitCodes.Contains(Direction.North)); if (scene.SouthConnection != null) { scene.ConnectedSceneGuids.Add(scene.SouthConnection.SceneHash); scene.ConnectedScenes.Add(scene.SouthConnection); } if (scene.WestConnection != null) { scene.ConnectedSceneGuids.Add(scene.WestConnection.SceneHash); scene.ConnectedScenes.Add(scene.WestConnection); } if (scene.NorthConnection != null) { scene.ConnectedSceneGuids.Add(scene.NorthConnection.SceneHash); scene.ConnectedScenes.Add(scene.NorthConnection); } if (scene.EastConnection != null) { scene.ConnectedSceneGuids.Add(scene.EastConnection.SceneHash); scene.ConnectedScenes.Add(scene.EastConnection); } }
internal static void UpdateSceneHistory() { if (ZetaDia.Me == null || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead) { return; } CachedScene currentScene; if (!_scenes.TryGetValue(GetSceneHash(ZetaDia.Me.CurrentScene.Mesh.Zone), out currentScene)) { return; } if (currentScene == null) { return; } if (_currentScene == null) { _currentScene = currentScene; return; } if (currentScene.SceneHash != _currentScene.SceneHash) { _previousScene = _currentScene; } _currentScene = currentScene; }
/// <summary> /// Returns the point at which two givens scenes connect; Vector3.Zero if they don't connect. /// </summary> internal static Vector3 GetSceneConnectionPoint(CachedScene targetScene, CachedScene thisScene = null) { thisScene = thisScene ?? CurrentScene; if (thisScene.IsConnectedEast && thisScene.EastConnection.Equals(targetScene)) { return(thisScene.EastEdgeCenter); } if (thisScene.IsConnectedWest && thisScene.WestConnection.Equals(targetScene)) { return(thisScene.WestEdgeCenter); } if (thisScene.IsConnectedSouth && thisScene.SouthConnection.Equals(targetScene)) { return(thisScene.SouthEdgeCenter); } if (thisScene.IsConnectedNorth && thisScene.NorthConnection.Equals(targetScene)) { return(thisScene.NorthEdgeCenter); } return(Vector3.Zero); }
/// <summary> /// Exit codes (the _NSEW_ part of a scene name) are used to link scenes together /// I am aware that some scenes (entrances/exit/special) scenes do not have these codes. /// Todo: address non-coded scenes. /// </summary> public static void PopulateExitCodes(CachedScene scene) { var exitCodes = SceneConnectionDirectionsRegex.Matches(scene.Name); foreach (var exitCode in from object exitCode in exitCodes select exitCode.ToString()) { var exitCount = exitCode.Trim('_').Count(); scene.ConnectionCount = exitCount; if (exitCount == 1) { scene.IsDeadEnd = true; } else if (exitCount == 2) { scene.IsCorridor = true; } else if (exitCount == 4 || exitCount == 3) { scene.IsMainHub = true; } foreach (var exitId in exitCode) { if (exitId == 'E') { scene.ExitCodes.Add(Direction.East); } if (exitId == 'W') { scene.ExitCodes.Add(Direction.West); } if (exitId == 'N') { scene.ExitCodes.Add(Direction.North); } if (exitId == 'S') { scene.ExitCodes.Add(Direction.South); } } } }
public void Render(SKCanvas canvas, float angleInRadians, SKPoint center, float scale) { if (_cachedScene == null) { _cachedScene = new CachedScene(); var paint = new SKPaint(); paint.IsAntialias = true; paint.Color = new SKColor(0x2c, 0x3e, 0x50); paint.StrokeCap = SKStrokeCap.Round; _cachedScene.LogoPaint = paint; _cachedScene.BackgroundRect = new SKRect(0, 0, 1080, 1800); _cachedScene.BackgroundPaint = new SKPaint { Color = new SKColor(255, 10, 10) }; _cachedScene.LogoPaths = new List <SKPath>(); for (int i = 1; i <= Columns; i++) { for (int y = 1; y <= Rows; y++) { var path = new SKPath(); path.MoveTo(71.4311121f, 56f); path.CubicTo(68.6763107f, 56.0058575f, 65.9796704f, 57.5737917f, 64.5928855f, 59.965729f); path.LineTo(43.0238921f, 97.5342563f); path.CubicTo(41.6587026f, 99.9325978f, 41.6587026f, 103.067402f, 43.0238921f, 105.465744f); path.LineTo(64.5928855f, 143.034271f); path.CubicTo(65.9798162f, 145.426228f, 68.6763107f, 146.994582f, 71.4311121f, 147f); path.LineTo(114.568946f, 147f); path.CubicTo(117.323748f, 146.994143f, 120.020241f, 145.426228f, 121.407172f, 143.034271f); path.LineTo(142.976161f, 105.465744f); path.CubicTo(144.34135f, 103.067402f, 144.341209f, 99.9325978f, 142.976161f, 97.5342563f); path.LineTo(121.407172f, 59.965729f); path.CubicTo(120.020241f, 57.5737917f, 117.323748f, 56.0054182f, 114.568946f, 56f); path.LineTo(71.4311121f, 56f); path.Close(); path.Offset(160 * i, 160 * y); _cachedScene.LogoPaths.Add(path); } } } canvas.Clear(SKColors.White); canvas.DrawRect(_cachedScene.BackgroundRect, _cachedScene.BackgroundPaint); foreach (var path in _cachedScene.LogoPaths) { canvas.DrawPath(path, _cachedScene.LogoPaint); } }
/// <summary> /// Returns a list of scenes between the source scene and the target scene. /// </summary> internal static List <CachedScene> GetPathToScene(CachedScene targetScene, CachedScene fromScene = null) { fromScene = fromScene ?? CurrentScene; var direction = MathUtil.GetDirectionToPoint(targetScene.Center, fromScene.Center); var checkedSceneHashes = new List <int>(); var path = new List <CachedScene>(); var found = false; var done = false; var currentScene = fromScene; const int depthStop = 20; var depth = 0; while (!found || depth > depthStop || done) { if (currentScene.IsConnectedTo(targetScene)) { path.Add(targetScene); found = true; } else { checkedSceneHashes.Add(currentScene.SceneHash); // Attempt to get new scene by direction var newScene = GetConnectedScene(direction, currentScene); // Failing that, just grab any connected scene if (checkedSceneHashes.Contains(newScene.SceneHash)) { newScene = currentScene.ConnectedScenes.FirstOrDefault(s => !checkedSceneHashes.Contains(s.SceneHash)); } if (newScene == null) { done = true; continue; } currentScene = newScene; path.Add(newScene); } depth++; } return(path); }
/// <summary> /// Returns a list of points between the source scene and the target scene. /// </summary> /// <param name="targetScene"></param> /// <param name="fromScene"></param> /// <returns></returns> internal static List <Vector3> GetVectorPathToScene(CachedScene targetScene, CachedScene fromScene = null) { fromScene = fromScene ?? CurrentScene; var scenePath = GetPathToScene(targetScene, fromScene); var vectorPath = new List <Vector3>(); var previousScene = fromScene; scenePath.ForEach(scene => { vectorPath.Add(GetSceneConnectionPoint(previousScene, scene)); vectorPath.Add(scene.Center); previousScene = scene; }); if (QuestToolsSettings.Instance.DebugEnabled) { Logger.Debug("Scene Route generated from {0} ({1}) to {2} ({3}) with {4} points", fromScene.Name, fromScene.SceneHash, targetScene.Name, targetScene.SceneHash, vectorPath.Count); vectorPath.ForEach(n => Logger.Log("Route Point: {0} Distance={1} Direction={2}", n, n.Distance(ZetaDia.Me.Position), MathUtil.GetDirectionToPoint(n))); } return(vectorPath); }
/// <summary> /// Returns a list of points that cover the closest walkable points to the four edges and center of the specified scene. /// </summary> internal static List <Vector3> GetSceneExploreRoute(CachedScene targetScene = null) { targetScene = targetScene ?? CurrentScene; var zone = targetScene.Scene.Mesh.Zone; var points = zone.NavZoneDef.NavCells.Where(n => n.IsValid && n.Flags.HasFlag(NavCellFlags.AllowWalk)).Select(n => GetNavCellCenter(n, zone)).ToList(); var route = new List <Vector3> { points.OrderBy(point => point.Distance2D(targetScene.Center)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.EastEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.NorthEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.SouthEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.WestEdgeCenter)).FirstOrDefault() }; if (QuestToolsSettings.Instance.DebugEnabled) { Logger.Debug("Scene Explore Route generated for {0} ({1}) with {2} points", targetScene.Name, targetScene.SceneHash, route.Count); route.ForEach(n => Logger.Log("Route Point: {0} Distance={1} Direction={2}", n, n.Distance(ZetaDia.Me.Position), MathUtil.GetDirectionToPoint(n))); } return(route); }
public CachedMapArea(Scene scene) { var temp = new CachedScene(scene); temp.UpdateNavcells(); ZoneMin = new Vector2(temp.ZoneMin.X, temp.ZoneMin.Y); ZoneMax = new Vector2(temp.ZoneMax.X, temp.ZoneMax.Y); foreach (var cell in temp.NavCells) { var tempCell = new CachedMapCell(cell); if (tempCell.Passable) { Cells.Add(tempCell); } } }
/// <summary> /// Exit codes (the _NSEW_ part of a scene name) are used to link scenes together /// I am aware that some scenes (entrances/exit/special) scenes do not have these codes. /// Todo: address non-coded scenes. /// </summary> public static void PopulateExitCodes(CachedScene scene) { var exitCodes = SceneConnectionDirectionsRegex.Matches(scene.Name); foreach (var exitCode in from object exitCode in exitCodes select exitCode.ToString()) { var exitCount = exitCode.Trim('_').Count(); scene.ConnectionCount = exitCount; if (exitCount == 1) scene.IsDeadEnd = true; else if (exitCount == 2) scene.IsCorridor = true; else if (exitCount == 4 || exitCount == 3) scene.IsMainHub = true; foreach (var exitId in exitCode) { if (exitId == 'E') scene.ExitCodes.Add(Direction.East); if (exitId == 'W') scene.ExitCodes.Add(Direction.West); if (exitId == 'N') scene.ExitCodes.Add(Direction.North); if (exitId == 'S') scene.ExitCodes.Add(Direction.South); } } }
/// <summary> /// Returns the point at which two givens scenes connect; Vector3.Zero if they don't connect. /// </summary> internal static Vector3 GetSceneConnectionPoint(CachedScene targetScene, CachedScene thisScene = null) { thisScene = thisScene ?? CurrentScene; if (thisScene.IsConnectedEast && thisScene.EastConnection.Equals(targetScene)) return thisScene.EastEdgeCenter; if (thisScene.IsConnectedWest && thisScene.WestConnection.Equals(targetScene)) return thisScene.WestEdgeCenter; if (thisScene.IsConnectedSouth && thisScene.SouthConnection.Equals(targetScene)) return thisScene.SouthEdgeCenter; if (thisScene.IsConnectedNorth && thisScene.NorthConnection.Equals(targetScene)) return thisScene.NorthEdgeCenter; return Vector3.Zero; }
internal static void UpdateSceneHistory() { if (ZetaDia.Me == null || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead) return; CachedScene currentScene; if (!_scenes.TryGetValue(GetSceneHash(ZetaDia.Me.CurrentScene.Mesh.Zone), out currentScene)) return; if (currentScene == null) return; if (_currentScene == null) { _currentScene = currentScene; return; } if (currentScene.SceneHash != _currentScene.SceneHash) _previousScene = _currentScene; _currentScene = currentScene; }
internal bool IsConnectedTo(CachedScene scene) { return(GetSceneConnectionPoint(this, scene) != Vector3.Zero); }
public static void Update() { var stopwatch = new Stopwatch(); stopwatch.Start(); var oldNodes = _nodes; var newScenes = ZetaDia.Scenes.Where(s => s.Mesh.Zone != null).ToList(); int minEdgeLength = (int)Math.Ceiling(newScenes.Min(s => Math.Min(s.Mesh.Zone.ZoneMax.X - s.Mesh.Zone.ZoneMin.X, s.Mesh.Zone.ZoneMax.Y - s.Mesh.Zone.ZoneMin.Y))); int halfEdgeLength = minEdgeLength / 2; var nodes = new List <DungeonNode>(); var cachedScenes = new List <CachedScene>(); var completeScenes = new HashSet <int>(_scenes.Where(pair => pair.Value.IsExplored).Select(pair => pair.Key)); // Iterate through scenes, find connecting scene names and create a dungeon node to navigate to the scene center newScenes.AsParallel().ForEach(scene => { if (scene.Name.EndsWith("_Filler") || !scene.IsValid) { return; } var zone = scene.Mesh.Zone; var zoneMin = zone.ZoneMin; var zoneMax = zone.ZoneMax; var sceneHash = GetSceneHash(zone); if (completeScenes.Contains(sceneHash)) { return; } var cachedScene = new CachedScene { Scene = scene, SceneHash = sceneHash, Name = scene.Name, Zone = zone, ZoneMax = zoneMax, ZoneMin = zoneMin, MinEdgeLength = minEdgeLength, HalfEdgeLength = halfEdgeLength }; PopulateExitCodes(cachedScene); var baseNode = new DungeonNode(zoneMin, zoneMax); cachedScene.BaseNode = baseNode; cachedScene.Nodes.Add(baseNode); if (nodes.All(node => node.WorldTopLeft != baseNode.WorldTopLeft)) { nodes.Add(baseNode); } cachedScene.Center = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.NorthEdgeCenter = new Vector3(zoneMin.X, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.SouthEdgeCenter = new Vector3(zoneMax.X, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.EastEdgeCenter = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMax.Y, 0); cachedScene.WestEdgeCenter = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMin.Y, 0); // North var northNode = (new DungeonNode(new Vector2(zoneMin.X - halfEdgeLength, zoneMin.Y), new Vector2(zoneMax.X - halfEdgeLength, zoneMin.Y))); cachedScene.NorthNode = northNode; cachedScene.Nodes.Add(northNode); if (nodes.All(node => node.WorldTopLeft != northNode.WorldTopLeft)) { nodes.Add(northNode); } // South var southNode = (new DungeonNode(new Vector2(zoneMin.X + halfEdgeLength, zoneMin.Y), new Vector2(zoneMax.X + halfEdgeLength, zoneMin.Y))); cachedScene.SouthNode = southNode; cachedScene.Nodes.Add(southNode); if (nodes.All(node => node.WorldTopLeft != southNode.WorldTopLeft)) { nodes.Add(southNode); } // East var eastNode = (new DungeonNode(new Vector2(zoneMin.X, zoneMin.Y - halfEdgeLength), new Vector2(zoneMax.X, zoneMin.Y - halfEdgeLength))); cachedScene.EastNode = eastNode; cachedScene.Nodes.Add(eastNode); if (nodes.All(node => node.WorldTopLeft != eastNode.WorldTopLeft)) { nodes.Add(eastNode); } // West var westNode = (new DungeonNode(new Vector2(zoneMin.X, zoneMin.Y + halfEdgeLength), new Vector2(zoneMax.X, zoneMin.Y + halfEdgeLength))); cachedScene.WestNode = westNode; cachedScene.Nodes.Add(westNode); if (nodes.All(node => node.WorldTopLeft != westNode.WorldTopLeft)) { nodes.Add(westNode); } if (!_scenes.ContainsKey(cachedScene.SceneHash)) { _scenes.Add(cachedScene.SceneHash, cachedScene); } else { _scenes[cachedScene.SceneHash] = cachedScene; } }); _scenes.Values.ForEach(PopulateSceneConnections); if (oldNodes != null && oldNodes.Any()) { nodes.AsParallel().ForEach(node => { var oldNode = oldNodes.FirstOrDefault(n => node.WorldTopLeft == n.WorldTopLeft); if (oldNode != null && oldNode.Visited) { node.Visited = true; } }); oldNodes.AsParallel().ForEach(oldNode => { if (nodes.All(newNode => newNode.Center != oldNode.WorldTopLeft)) { nodes.Add(oldNode); } }); } SceneHistory.UpdateSceneHistory(); _nodes = new ConcurrentBag <DungeonNode>(nodes.Distinct()); Logger.Debug("Updated SceneSegmentation {0} Scenes {1} nodes in {2:0}ms", _scenes.Count, _nodes.Count, stopwatch.ElapsedMilliseconds); }
/// <summary> /// The closest scene that has its one or more of its connected scenes undiscovered. /// </summary> internal static CachedScene GetNearestUnexploredScene(CachedScene thisScene = null) { var scene = thisScene ?? CurrentScene; return(Scenes.Where(s => !s.IsExplored).OrderBy(s => scene.Center.Distance2D(s.Center)).FirstOrDefault()); }
public static void Update() { var stopwatch = new Stopwatch(); stopwatch.Start(); var oldNodes = _nodes; var newScenes = ZetaDia.Scenes.Where(s => s.Mesh.Zone != null).ToList(); int minEdgeLength = (int)Math.Ceiling(newScenes.Min(s => Math.Min(s.Mesh.Zone.ZoneMax.X - s.Mesh.Zone.ZoneMin.X, s.Mesh.Zone.ZoneMax.Y - s.Mesh.Zone.ZoneMin.Y))); int halfEdgeLength = minEdgeLength / 2; var nodes = new List<DungeonNode>(); var cachedScenes = new List<CachedScene>(); var completeScenes = new HashSet<int>(_scenes.Where(pair => pair.Value.IsExplored).Select(pair => pair.Key)); // Iterate through scenes, find connecting scene names and create a dungeon node to navigate to the scene center newScenes.AsParallel().ForEach(scene => { if (scene.Name.EndsWith("_Filler") || !scene.IsValid) return; var zone = scene.Mesh.Zone; var zoneMin = zone.ZoneMin; var zoneMax = zone.ZoneMax; var sceneHash = GetSceneHash(zone); if (completeScenes.Contains(sceneHash)) return; var cachedScene = new CachedScene { Scene = scene, SceneHash = sceneHash, Name = scene.Name, Zone = zone, ZoneMax = zoneMax, ZoneMin = zoneMin, MinEdgeLength = minEdgeLength, HalfEdgeLength = halfEdgeLength }; PopulateExitCodes(cachedScene); var baseNode = new DungeonNode(zoneMin, zoneMax); cachedScene.BaseNode = baseNode; cachedScene.Nodes.Add(baseNode); if (nodes.All(node => node.WorldTopLeft != baseNode.WorldTopLeft)) nodes.Add(baseNode); cachedScene.Center = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.NorthEdgeCenter = new Vector3(zoneMin.X, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.SouthEdgeCenter = new Vector3(zoneMax.X, zoneMax.Y - (zoneMax.Y - zoneMin.Y) / 2, 0); cachedScene.EastEdgeCenter = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMax.Y, 0); cachedScene.WestEdgeCenter = new Vector3(zoneMax.X - (zoneMax.X - zoneMin.X) / 2, zoneMin.Y, 0); // North var northNode = (new DungeonNode(new Vector2(zoneMin.X - halfEdgeLength, zoneMin.Y), new Vector2(zoneMax.X - halfEdgeLength, zoneMin.Y))); cachedScene.NorthNode = northNode; cachedScene.Nodes.Add(northNode); if (nodes.All(node => node.WorldTopLeft != northNode.WorldTopLeft)) nodes.Add(northNode); // South var southNode = (new DungeonNode(new Vector2(zoneMin.X + halfEdgeLength, zoneMin.Y), new Vector2(zoneMax.X + halfEdgeLength, zoneMin.Y))); cachedScene.SouthNode = southNode; cachedScene.Nodes.Add(southNode); if (nodes.All(node => node.WorldTopLeft != southNode.WorldTopLeft)) nodes.Add(southNode); // East var eastNode = (new DungeonNode(new Vector2(zoneMin.X, zoneMin.Y - halfEdgeLength), new Vector2(zoneMax.X, zoneMin.Y - halfEdgeLength))); cachedScene.EastNode = eastNode; cachedScene.Nodes.Add(eastNode); if (nodes.All(node => node.WorldTopLeft != eastNode.WorldTopLeft)) nodes.Add(eastNode); // West var westNode = (new DungeonNode(new Vector2(zoneMin.X, zoneMin.Y + halfEdgeLength), new Vector2(zoneMax.X, zoneMin.Y + halfEdgeLength))); cachedScene.WestNode = westNode; cachedScene.Nodes.Add(westNode); if (nodes.All(node => node.WorldTopLeft != westNode.WorldTopLeft)) nodes.Add(westNode); if (!_scenes.ContainsKey(cachedScene.SceneHash)) _scenes.Add(cachedScene.SceneHash, cachedScene); else _scenes[cachedScene.SceneHash] = cachedScene; }); _scenes.Values.ForEach(PopulateSceneConnections); if (oldNodes != null && oldNodes.Any()) { nodes.AsParallel().ForEach(node => { var oldNode = oldNodes.FirstOrDefault(n => node.WorldTopLeft == n.WorldTopLeft); if (oldNode != null && oldNode.Visited) node.Visited = true; }); oldNodes.AsParallel().ForEach(oldNode => { if (nodes.All(newNode => newNode.Center != oldNode.WorldTopLeft)) { nodes.Add(oldNode); } }); } SceneHistory.UpdateSceneHistory(); _nodes = new ConcurrentBag<DungeonNode>(nodes.Distinct()); Logger.Debug("Updated SceneSegmentation {0} Scenes {1} nodes in {2:0}ms", _scenes.Count, _nodes.Count, stopwatch.ElapsedMilliseconds); }
/// <summary> /// Returns a scene that is directly connected to the specified scene; avoids DeadEnds and leans towards the specified direction. /// </summary> /// <param name="direction">the long range direction you want to go</param> /// <param name="thisScene">source scene</param> /// <param name="avoidPreviousScenes">whether to avoid scenes you've been in recently</param> /// <returns></returns> internal static CachedScene GetConnectedScene(Direction direction = Direction.Any, CachedScene thisScene = null, bool avoidPreviousScenes = true) { var scene = thisScene ?? CurrentScene; var previousScene = SceneHistory.PreviousScene; CachedScene result = null; switch (direction) { case Direction.North: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) result = scene.NorthConnection; else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.EastConnection.SceneHash)) result = scene.EastConnection; else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) result = scene.WestConnection; break; case Direction.South: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) result = scene.SouthConnection; else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.EastConnection.SceneHash)) result = scene.EastConnection; else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) result = scene.WestConnection; break; case Direction.East: if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) result = scene.EastConnection; else if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.SouthConnection.SceneHash)) result = scene.SouthConnection; else if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) result = scene.NorthConnection; break; case Direction.West: if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) result = scene.WestConnection; else if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.SouthConnection.SceneHash)) result = scene.SouthConnection; else if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) result = scene.NorthConnection; break; case Direction.SouthEast: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) result = scene.SouthConnection; else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) result = scene.EastConnection; break; case Direction.NorthEast: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) result = scene.NorthConnection; else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) result = scene.EastConnection; break; case Direction.SouthWest: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) result = scene.SouthConnection; else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) result = scene.WestConnection; break; case Direction.NorthWest: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) result = scene.NorthConnection; else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) result = scene.WestConnection; break; } return result ?? scene.ConnectedScenes.FirstOrDefault(); }
/// <summary> /// The closest scene that has its one or more of its connected scenes undiscovered. /// </summary> internal static CachedScene GetNearestUnexploredScene(CachedScene thisScene = null) { var scene = thisScene ?? CurrentScene; return Scenes.Where(s => !s.IsExplored).OrderBy(s => scene.Center.Distance2D(s.Center)).FirstOrDefault(); }
/// <summary> /// Returns a list of scenes between the source scene and the target scene. /// </summary> internal static List<CachedScene> GetPathToScene(CachedScene targetScene, CachedScene fromScene = null) { fromScene = fromScene ?? CurrentScene; var direction = MathUtil.GetDirectionToPoint(targetScene.Center, fromScene.Center); var checkedSceneHashes = new List<int>(); var path = new List<CachedScene>(); var found = false; var done = false; var currentScene = fromScene; const int depthStop = 20; var depth = 0; while (!found || depth > depthStop || done) { if (currentScene.IsConnectedTo(targetScene)) { path.Add(targetScene); found = true; } else { checkedSceneHashes.Add(currentScene.SceneHash); // Attempt to get new scene by direction var newScene = GetConnectedScene(direction, currentScene); // Failing that, just grab any connected scene if (checkedSceneHashes.Contains(newScene.SceneHash)) newScene = currentScene.ConnectedScenes.FirstOrDefault(s => !checkedSceneHashes.Contains(s.SceneHash)); if (newScene == null) { done = true; continue; } currentScene = newScene; path.Add(newScene); } depth++; } return path; }
/// <summary> /// Returns a list of points between the source scene and the target scene. /// </summary> /// <param name="targetScene"></param> /// <param name="fromScene"></param> /// <returns></returns> internal static List<Vector3> GetVectorPathToScene(CachedScene targetScene, CachedScene fromScene = null) { fromScene = fromScene ?? CurrentScene; var scenePath = GetPathToScene(targetScene, fromScene); var vectorPath = new List<Vector3>(); var previousScene = fromScene; scenePath.ForEach(scene => { vectorPath.Add(GetSceneConnectionPoint(previousScene, scene)); vectorPath.Add(scene.Center); previousScene = scene; }); if (QuestToolsSettings.Instance.DebugEnabled) { Logger.Debug("Scene Route generated from {0} ({1}) to {2} ({3}) with {4} points", fromScene.Name, fromScene.SceneHash, targetScene.Name, targetScene.SceneHash, vectorPath.Count); vectorPath.ForEach(n => Logger.Log("Route Point: {0} Distance={1} Direction={2}", n, n.Distance(ZetaDia.Me.Position), MathUtil.GetDirectionToPoint(n))); } return vectorPath; }
/// <summary> /// Returns a list of points that cover the closest walkable points to the four edges and center of the specified scene. /// </summary> internal static List<Vector3> GetSceneExploreRoute(CachedScene targetScene = null) { targetScene = targetScene ?? CurrentScene; var zone = targetScene.Scene.Mesh.Zone; var points = zone.NavZoneDef.NavCells.Where(n => n.IsValid && n.Flags.HasFlag(NavCellFlags.AllowWalk)).Select(n => GetNavCellCenter(n, zone)).ToList(); var route = new List<Vector3> { points.OrderBy(point => point.Distance2D(targetScene.Center)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.EastEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.NorthEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.SouthEdgeCenter)).FirstOrDefault(), points.OrderBy(point => point.Distance2D(targetScene.WestEdgeCenter)).FirstOrDefault() }; if (QuestToolsSettings.Instance.DebugEnabled) { Logger.Debug("Scene Explore Route generated for {0} ({1}) with {2} points", targetScene.Name, targetScene.SceneHash, route.Count); route.ForEach(n => Logger.Log("Route Point: {0} Distance={1} Direction={2}", n, n.Distance(ZetaDia.Me.Position), MathUtil.GetDirectionToPoint(n))); } return route; }
/// <summary> /// Returns a scene that is directly connected to the specified scene; avoids DeadEnds and leans towards the specified direction. /// </summary> /// <param name="direction">the long range direction you want to go</param> /// <param name="thisScene">source scene</param> /// <param name="avoidPreviousScenes">whether to avoid scenes you've been in recently</param> /// <returns></returns> internal static CachedScene GetConnectedScene(Direction direction = Direction.Any, CachedScene thisScene = null, bool avoidPreviousScenes = true) { var scene = thisScene ?? CurrentScene; var previousScene = SceneHistory.PreviousScene; CachedScene result = null; switch (direction) { case Direction.North: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) { result = scene.NorthConnection; } else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.EastConnection.SceneHash)) { result = scene.EastConnection; } else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) { result = scene.WestConnection; } break; case Direction.South: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) { result = scene.SouthConnection; } else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.EastConnection.SceneHash)) { result = scene.EastConnection; } else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) { result = scene.WestConnection; } break; case Direction.East: if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) { result = scene.EastConnection; } else if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.SouthConnection.SceneHash)) { result = scene.SouthConnection; } else if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) { result = scene.NorthConnection; } break; case Direction.West: if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) { result = scene.WestConnection; } else if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd && (!avoidPreviousScenes || previousScene.SceneHash != scene.SouthConnection.SceneHash)) { result = scene.SouthConnection; } else if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) { result = scene.NorthConnection; } break; case Direction.SouthEast: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) { result = scene.SouthConnection; } else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) { result = scene.EastConnection; } break; case Direction.NorthEast: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) { result = scene.NorthConnection; } else if (scene.IsConnectedEast && !scene.EastConnection.IsDeadEnd) { result = scene.EastConnection; } break; case Direction.SouthWest: if (scene.IsConnectedSouth && !scene.SouthConnection.IsDeadEnd) { result = scene.SouthConnection; } else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) { result = scene.WestConnection; } break; case Direction.NorthWest: if (scene.IsConnectedNorth && !scene.NorthConnection.IsDeadEnd) { result = scene.NorthConnection; } else if (scene.IsConnectedWest && !scene.WestConnection.IsDeadEnd) { result = scene.WestConnection; } break; } return(result ?? scene.ConnectedScenes.FirstOrDefault()); }
internal bool IsConnectedTo(CachedScene scene) { return GetSceneConnectionPoint(this, scene) != Vector3.Zero; }