Advertisement
Guest User

Untitled

a guest
Apr 14th, 2014
256
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 65.24 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using Trinity.DbProvider;
  6. using Trinity.Technicals;
  7. using Zeta.Bot.Dungeons;
  8. using Zeta.Bot.Logic;
  9. using Zeta.Bot.Navigation;
  10. using Zeta.Bot.Pathfinding;
  11. using Zeta.Bot.Profile;
  12. using Zeta.Common;
  13. using Zeta.Game;
  14. using Zeta.Game.Internals;
  15. using Zeta.Game.Internals.Actors;
  16. using Zeta.Game.Internals.SNO;
  17. using Zeta.TreeSharp;
  18. using Zeta.XmlEngine;
  19. using Action = Zeta.TreeSharp.Action;
  20. using Logger = Trinity.Technicals.Logger;
  21.  
  22. namespace Trinity.XmlTags
  23. {
  24. /// <summary>
  25. /// TrinityExploreDungeon is fuly backwards compatible with the built-in Demonbuddy ExploreArea tag. It provides additional features such as:
  26. /// Moving to investigate MiniMapMarker pings and the current ExitNameHash if provided and visible (mini map marker 0 and the current exitNameHash)
  27. /// Moving to investigate Priority Scenes if provided (PrioritizeScenes subtags)
  28. /// Ignoring DungeonExplorer nodes in certain scenes if provided (IgnoreScenes subtags)
  29. /// Reduced backtracking (via pathPrecision attribute and combat skip ahead cache)
  30. /// Multiple ActorId's for the ObjectFound end type (AlternateActors sub-tags)
  31. /// </summary>
  32. [XmlElement("TrinityExploreDungeon")]
  33. public class TrinityExploreDungeon : ProfileBehavior
  34. {
  35. /// <summary>
  36. /// The SNOId of the Actor that we're looking for, used with until="ObjectFound"
  37. /// </summary>
  38. [XmlAttribute("actorId", true)]
  39. public int ActorId { get; set; }
  40.  
  41. /// <summary>
  42. /// Sets a custom grid segmentation Box Size (default 15)
  43. /// </summary>
  44. [XmlAttribute("boxSize", true)]
  45. public int BoxSize { get; set; }
  46.  
  47. /// <summary>
  48. /// Sets a custom grid segmentation Box Tolerance (default 0.55)
  49. /// </summary>
  50. [XmlAttribute("boxTolerance", true)]
  51. public float BoxTolerance { get; set; }
  52.  
  53. /// <summary>
  54. /// The nameHash of the exit the bot will move to and finish the tag when found
  55. /// </summary>
  56. [XmlAttribute("exitNameHash", true)]
  57. public int ExitNameHash { get; set; }
  58.  
  59. [XmlAttribute("ignoreGridReset", true)]
  60. public bool IgnoreGridReset { get; set; }
  61.  
  62. /// <summary>
  63. /// Not currently implimented
  64. /// </summary>
  65. [XmlAttribute("leaveWhenFinished", true)]
  66. public bool LeaveWhenExplored { get; set; }
  67.  
  68. /// <summary>
  69. /// The distance the bot must be from an actor before marking the tag as complete, when used with until="ObjectFound"
  70. /// </summary>
  71. [XmlAttribute("objectDistance", true)]
  72. public float ObjectDistance { get; set; }
  73.  
  74. /// <summary>
  75. /// The until="" atribute must match one of these
  76. /// </summary>
  77. public enum TrinityExploreEndType
  78. {
  79. FullyExplored = 0,
  80. ObjectFound,
  81. ExitFound,
  82. SceneFound,
  83. SceneLeftOrActorFound
  84. }
  85.  
  86. [XmlAttribute("endType", true)]
  87. [XmlAttribute("until", true)]
  88. public TrinityExploreEndType EndType { get; set; }
  89.  
  90. /// <summary>
  91. /// The list of Scene SNOId's or Scene Names that the bot will ignore dungeon nodes in
  92. /// </summary>
  93. [XmlElement("IgnoreScenes")]
  94. public List<IgnoreScene> IgnoreScenes { get; set; }
  95.  
  96. /// <summary>
  97. /// The list of Scene SNOId's or Scene Names that the bot will prioritize (only works when the scene is "loaded")
  98. /// </summary>
  99. [XmlElement("PriorityScenes")]
  100. [XmlElement("PrioritizeScenes")]
  101. public List<PrioritizeScene> PriorityScenes { get; set; }
  102.  
  103. /// <summary>
  104. /// The list of Scene SNOId's or Scene Names that the bot will use for endtype SceneLeftOrActorFound
  105. /// </summary>
  106. [XmlElement("AlternateScenes")]
  107. public List<AlternateScene> AlternateScenes { get; set; }
  108.  
  109. /// <summary>
  110. /// The Ignore Scene class, used as IgnoreScenes child elements
  111. /// </summary>
  112. [XmlElement("IgnoreScene")]
  113. public class IgnoreScene : IEquatable<Scene>
  114. {
  115. [XmlAttribute("sceneName")]
  116. public string SceneName { get; set; }
  117. [XmlAttribute("sceneId")]
  118. public int SceneId { get; set; }
  119.  
  120. public IgnoreScene()
  121. {
  122. SceneId = -1;
  123. SceneName = String.Empty;
  124. }
  125.  
  126. public IgnoreScene(string name)
  127. {
  128. this.SceneName = name;
  129. }
  130. public IgnoreScene(int id)
  131. {
  132. this.SceneId = id;
  133. }
  134.  
  135. public bool Equals(Scene other)
  136. {
  137. return (!string.IsNullOrWhiteSpace(SceneName) && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
  138. }
  139. }
  140.  
  141. private CachedValue<List<Area>> m_IgnoredAreas;
  142. private List<Area> IgnoredAreas
  143. {
  144. get
  145. {
  146. if (m_IgnoredAreas == null)
  147. m_IgnoredAreas = new CachedValue<List<Area>>(() => { return GetIgnoredAreas(); }, TimeSpan.FromSeconds(1));
  148. return m_IgnoredAreas.Value;
  149. }
  150. }
  151.  
  152. private List<Area> GetIgnoredAreas()
  153. {
  154. var ignoredScenes = ZetaDia.Scenes.GetScenes()
  155. .Where(scn => scn.IsValid && IgnoreScenes.Any(igns => igns.Equals(scn)) && !PriorityScenes.Any(psc => psc.Equals(scn)))
  156. .Select(scn =>
  157. scn.Mesh.Zone == null
  158. ? new Area(new Vector2(float.MinValue, float.MinValue), new Vector2(float.MaxValue, float.MaxValue))
  159. : new Area(scn.Mesh.Zone.ZoneMin, scn.Mesh.Zone.ZoneMax))
  160. .ToList();
  161. Logger.Log(LogCategory.ProfileTag, "Returning {0} ignored areas", ignoredScenes.Count());
  162. return ignoredScenes;
  163. }
  164.  
  165. private class Area
  166. {
  167. public Vector2 Min { get; set; }
  168. public Vector2 Max { get; set; }
  169.  
  170. /// <summary>
  171. /// Initializes a new instance of the Area class.
  172. /// </summary>
  173. public Area(Vector2 min, Vector2 max)
  174. {
  175. Min = min;
  176. Max = max;
  177. }
  178.  
  179. public bool IsPositionInside(Vector2 position)
  180. {
  181. return position.X >= Min.X && position.X <= Max.X && position.Y >= Min.Y && position.Y <= Max.Y;
  182. }
  183.  
  184. public bool IsPositionInside(Vector3 position)
  185. {
  186. return IsPositionInside(position.ToVector2());
  187. }
  188. }
  189.  
  190. /// <summary>
  191. /// The Priority Scene class, used as PrioritizeScenes child elements
  192. /// </summary>
  193. [XmlElement("PriorityScene")]
  194. [XmlElement("PrioritizeScene")]
  195. public class PrioritizeScene : IEquatable<Scene>
  196. {
  197. [XmlAttribute("sceneName")]
  198. public string SceneName { get; set; }
  199. [XmlAttribute("sceneId")]
  200. public int SceneId { get; set; }
  201. [XmlAttribute("pathPrecision")]
  202. public float PathPrecision { get; set; }
  203.  
  204. public PrioritizeScene()
  205. {
  206. PathPrecision = 15f;
  207. SceneName = String.Empty;
  208. SceneId = -1;
  209. }
  210.  
  211. public PrioritizeScene(string name)
  212. {
  213. this.SceneName = name;
  214. }
  215. public PrioritizeScene(int id)
  216. {
  217. this.SceneId = id;
  218. }
  219. public bool Equals(Scene other)
  220. {
  221. return (SceneName != String.Empty && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
  222. }
  223. }
  224.  
  225. /// <summary>
  226. /// The Alternate Scene class, used as AlternateScenes child elements
  227. /// </summary>
  228. [XmlElement("AlternateScene")]
  229. public class AlternateScene : IEquatable<Scene>
  230. {
  231. [XmlAttribute("sceneName")]
  232. public string SceneName { get; set; }
  233. [XmlAttribute("sceneId")]
  234. public int SceneId { get; set; }
  235. [XmlAttribute("pathPrecision")]
  236. public float PathPrecision { get; set; }
  237.  
  238. public AlternateScene()
  239. {
  240. PathPrecision = 15f;
  241. SceneName = String.Empty;
  242. SceneId = -1;
  243. }
  244.  
  245. public AlternateScene(string name)
  246. {
  247. this.SceneName = name;
  248. }
  249. public AlternateScene(int id)
  250. {
  251. this.SceneId = id;
  252. }
  253. public bool Equals(Scene other)
  254. {
  255. return (SceneName != String.Empty && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
  256. }
  257. }
  258.  
  259. [XmlElement("AlternateActors")]
  260. public List<AlternateActor> AlternateActors { get; set; }
  261.  
  262. [XmlElement("AlternateActor")]
  263. public class AlternateActor
  264. {
  265. [XmlAttribute("actorId")]
  266. public int ActorId { get; set; }
  267.  
  268. [XmlAttribute("objectDistance")]
  269. public float ObjectDistance { get; set; }
  270.  
  271. [XmlAttribute("interactRange")]
  272. public float InteractRange { get; set; }
  273.  
  274. public AlternateActor()
  275. {
  276. ActorId = -1;
  277. ObjectDistance = 60f;
  278. }
  279. }
  280.  
  281. [XmlElement("Objectives")]
  282. public List<Objective> Objectives { get; set; }
  283.  
  284. [XmlElement("Objective")]
  285. public class Objective
  286. {
  287. [XmlAttribute("actorId")]
  288. public int ActorID { get; set; }
  289.  
  290. [XmlAttribute("markerNameHash")]
  291. public int MarkerNameHash { get; set; }
  292.  
  293. [XmlAttribute("count")]
  294. public int Count { get; set; }
  295.  
  296. [XmlAttribute("endAnimation")]
  297. public SNOAnim EndAnimation { get; set; }
  298.  
  299. [XmlAttribute("interact")]
  300. public bool Interact { get; set; }
  301.  
  302. public Objective()
  303. {
  304.  
  305. }
  306. }
  307.  
  308. /// <summary>
  309. /// The Scene SNOId, used with ExploreUntil="SceneFound"
  310. /// </summary>
  311. [XmlAttribute("sceneId")]
  312. public int SceneId { get; set; }
  313.  
  314. /// <summary>
  315. /// The Scene Name, used with ExploreUntil="SceneFound", a sub-string match will work
  316. /// </summary>
  317. [XmlAttribute("sceneName")]
  318. public string SceneName { get; set; }
  319.  
  320. /// <summary>
  321. /// The distance the bot will mark dungeon nodes as "visited" (default is 1/2 of box size, minimum 10)
  322. /// </summary>
  323. [XmlAttribute("pathPrecision")]
  324. public float PathPrecision { get; set; }
  325.  
  326. /// <summary>
  327. /// The distance before reaching a MiniMapMarker before marking it as visited
  328. /// </summary>
  329. [XmlAttribute("markerDistance")]
  330. public float MarkerDistance { get; set; }
  331.  
  332. /// <summary>
  333. /// Disable Mini Map Marker Scouting
  334. /// </summary>
  335. [XmlAttribute("ignoreMarkers")]
  336. public bool IgnoreMarkers { get; set; }
  337.  
  338. public enum TimeoutType
  339. {
  340. Timer,
  341. GoldInactivity,
  342. None,
  343. }
  344.  
  345. /// <summary>
  346. /// The TimeoutType to use (default None, no timeout)
  347. /// </summary>
  348. [XmlAttribute("timeoutType")]
  349. public TimeoutType ExploreTimeoutType { get; set; }
  350.  
  351. /// <summary>
  352. /// Value in Seconds.
  353. /// The timeout value to use, when used with Timer will force-end the tag after a certain time. When used with GoldInactivity will end the tag after coinages doesn't change for the given period
  354. /// </summary>
  355. [XmlAttribute("timeoutValue")]
  356. public int TimeoutValue { get; set; }
  357.  
  358. /// <summary>
  359. /// If we want to use a townportal before ending the tag when a timeout happens
  360. /// </summary>
  361. [XmlAttribute("townPortalOnTimeout")]
  362. public bool TownPortalOnTimeout { get; set; }
  363.  
  364. /// <summary>
  365. /// Ignore last N nodes of dungeon explorer, when using endType=FullyExplored
  366. /// </summary>
  367. [XmlAttribute("ignoreLastNodes")]
  368. public int IgnoreLastNodes { get; set; }
  369.  
  370. /// <summary>
  371. /// Used with IgnoreLastNodes, minimum visited node count before tag can end.
  372. /// The minVisistedNodes is purely, and only for use with ignoreLastNodes - it does not serve any other function like you expect.
  373. /// The reason this attribute exists, is to prevent prematurely exiting the dungeon exploration when used with ignoreLastNodes.
  374. /// For example, when the bot first starts exploring an area, it needs to navigate a few dungeon nodes first before other dungeon nodes even appear - otherwise with ignoreLastNodes > 2,
  375. /// the bot would immediately exit from navigation without exploring anything at all.
  376. /// </summary>
  377. [XmlAttribute("minVisitedNodes")]
  378. public int MinVisistedNodes { get; set; }
  379.  
  380. [XmlAttribute("SetNodesExploredAutomatically")]
  381. [XmlAttribute("setNodesExploredAutomatically")]
  382. public bool SetNodesExploredAutomatically { get; set; }
  383.  
  384. [XmlAttribute("minObjectOccurances")]
  385. public int MinOccurances { get; set; }
  386.  
  387. [XmlAttribute("interactWithObject")]
  388. public bool InteractWithObject { get; set; }
  389.  
  390. [XmlAttribute("interactRange")]
  391. public float ObjectInteractRange { get; set; }
  392.  
  393. [XmlAttribute("stayAfterBounty", true)]
  394. public bool StayAfterBounty { get; set; }
  395.  
  396. HashSet<Tuple<int, Vector3>> foundObjects = new HashSet<Tuple<int, Vector3>>();
  397.  
  398. /// <summary>
  399. /// The Position of the CurrentNode NavigableCenter
  400. /// </summary>
  401. private Vector3 CurrentNavTarget
  402. {
  403. get
  404. {
  405. if (PrioritySceneTarget != Vector3.Zero)
  406. {
  407. return PrioritySceneTarget;
  408. }
  409.  
  410. if (GetRouteUnvisitedNodeCount() > 0)
  411. {
  412. return BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter;
  413. }
  414. else
  415. {
  416. return Vector3.Zero;
  417. }
  418. }
  419. }
  420.  
  421. // Adding these for SimpleFollow compatability
  422. public float X { get { return CurrentNavTarget.X; } }
  423. public float Y { get { return CurrentNavTarget.Y; } }
  424. public float Z { get { return CurrentNavTarget.Z; } }
  425.  
  426. private bool InitDone = false;
  427. private DungeonNode NextNode;
  428.  
  429. /// <summary>
  430. /// The current player position
  431. /// </summary>
  432. private Vector3 myPos { get { return Trinity.Player.Position; } }
  433. private static ISearchAreaProvider MainGridProvider
  434. {
  435. get
  436. {
  437. return Trinity.MainGridProvider;
  438. }
  439. }
  440.  
  441. /// <summary>
  442. /// The last scene SNOId we entered
  443. /// </summary>
  444. private int mySceneId = -1;
  445. /// <summary>
  446. /// The last position we updated the ISearchGridProvider at
  447. /// </summary>
  448. private Vector3 GPUpdatePosition = Vector3.Zero;
  449.  
  450. /// <summary>
  451. /// Called when the profile behavior starts
  452. /// </summary>
  453. public override void OnStart()
  454. {
  455. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "TrinityExploreDungeon OnStart() called");
  456.  
  457. if (SetNodesExploredAutomatically)
  458. {
  459. Logger.Log(LogCategory.ProfileTag, "Minimap Explored Nodes Enabled");
  460. BrainBehavior.DungeonExplorer.SetNodesExploredAutomatically = true;
  461. }
  462. else
  463. {
  464. Logger.Log(LogCategory.ProfileTag, "Minimap Explored Nodes Disabled");
  465. BrainBehavior.DungeonExplorer.SetNodesExploredAutomatically = false;
  466. }
  467.  
  468. if (!IgnoreGridReset)
  469. {
  470. UpdateSearchGridProvider();
  471.  
  472. CheckResetDungeonExplorer();
  473.  
  474. GridSegmentation.Reset();
  475. BrainBehavior.DungeonExplorer.Reset();
  476. MiniMapMarker.KnownMarkers.Clear();
  477. }
  478.  
  479. if (!InitDone)
  480. {
  481. Init();
  482. }
  483. TagTimer.Reset();
  484. timesForcedReset = 0;
  485.  
  486. if (Objectives == null)
  487. Objectives = new List<Objective>();
  488.  
  489. if (ObjectDistance == 0)
  490. ObjectDistance = 25f;
  491.  
  492. PrintNodeCounts("PostInit");
  493. }
  494.  
  495. /// <summary>
  496. /// Re-sets the DungeonExplorer, BoxSize, BoxTolerance, and Updates the current route
  497. /// </summary>
  498. private void CheckResetDungeonExplorer()
  499. {
  500. if (!ZetaDia.IsInGame || ZetaDia.IsLoadingWorld || !ZetaDia.WorldInfo.IsValid || !ZetaDia.Scenes.IsValid || !ZetaDia.Service.IsValid)
  501. return;
  502.  
  503. // I added this because GridSegmentation may (rarely) reset itself without us doing it to 15/.55.
  504. if ((BoxSize != 0 && BoxTolerance != 0) && (GridSegmentation.BoxSize != BoxSize || GridSegmentation.BoxTolerance != BoxTolerance) || (GetGridSegmentationNodeCount() == 0))
  505. {
  506. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Box Size or Tolerance has been changed! {0}/{1}", GridSegmentation.BoxSize, GridSegmentation.BoxTolerance);
  507.  
  508. BrainBehavior.DungeonExplorer.Reset();
  509. PrintNodeCounts("BrainBehavior.DungeonExplorer.Reset");
  510.  
  511. GridSegmentation.BoxSize = BoxSize;
  512. GridSegmentation.BoxTolerance = BoxTolerance;
  513. PrintNodeCounts("SetBoxSize+Tolerance");
  514.  
  515. BrainBehavior.DungeonExplorer.Update();
  516. PrintNodeCounts("BrainBehavior.DungeonExplorer.Update");
  517. }
  518. }
  519.  
  520. /// <summary>
  521. /// The main profile behavior
  522. /// </summary>
  523. /// <returns></returns>
  524. protected override Composite CreateBehavior()
  525. {
  526. return
  527. new Sequence(
  528. new DecoratorContinue(ret => !IgnoreMarkers,
  529. new Sequence(
  530. MiniMapMarker.DetectMiniMapMarkers(0),
  531. MiniMapMarker.DetectMiniMapMarkers(ExitNameHash),
  532. MiniMapMarker.DetectMiniMapMarkers(Objectives)
  533. )
  534. ),
  535. UpdateSearchGridProvider(),
  536. new Action(ret => CheckResetDungeonExplorer()),
  537. new PrioritySelector(
  538. CheckIsObjectiveFinished(),
  539. PrioritySceneCheck(),
  540. new Decorator(ret => !IgnoreMarkers,
  541. MiniMapMarker.VisitMiniMapMarkers(myPos, MarkerDistance)
  542. ),
  543. new Decorator(ret => ShouldInvestigateActor(),
  544. new PrioritySelector(
  545. new Decorator(ret => CurrentActor != null && CurrentActor.IsValid &&
  546. Objectives.Any(o => o.ActorID == CurrentActor.ActorSNO && o.Interact) &&
  547. CurrentActor.Position.Distance(Trinity.Player.Position) <= CurrentActor.CollisionSphere.Radius,
  548. new Sequence(
  549. new Action(ret => CurrentActor.Interact())
  550. )
  551. ),
  552. InvestigateActor()
  553. )
  554. ),
  555. new Sequence(
  556. new DecoratorContinue(ret => DungeonRouteIsEmpty(),
  557. new Action(ret => UpdateRoute())
  558. ),
  559. CheckIsExplorerFinished()
  560. ),
  561. new DecoratorContinue(ret => DungeonRouteIsValid(),
  562. new PrioritySelector(
  563. CheckNodeFinished(),
  564. new Sequence(
  565. new Action(ret => PrintNodeCounts("MainBehavior")),
  566. new Action(ret => MoveToNextNode())
  567. )
  568. )
  569. ),
  570. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Error 1: Unknown error occured!"))
  571. )
  572. );
  573. }
  574.  
  575. private static bool DungeonRouteIsValid()
  576. {
  577. return BrainBehavior.DungeonExplorer != null && BrainBehavior.DungeonExplorer.CurrentRoute != null && BrainBehavior.DungeonExplorer.CurrentRoute.Any();
  578. }
  579.  
  580. private static bool DungeonRouteIsEmpty()
  581. {
  582. return BrainBehavior.DungeonExplorer != null && BrainBehavior.DungeonExplorer.CurrentRoute != null && !BrainBehavior.DungeonExplorer.CurrentRoute.Any();
  583. }
  584.  
  585. private bool CurrentActorIsFinished
  586. {
  587. get
  588. {
  589. return Objectives.Any(o => o.ActorID == CurrentActor.ActorSNO && o.EndAnimation == CurrentActor.CommonData.CurrentAnimation);
  590. }
  591. }
  592.  
  593. private DiaObject CurrentActor
  594. {
  595. get
  596. {
  597. var actor =
  598. ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false)
  599. .Where(a => (a.ActorSNO == ActorId ||
  600. Objectives.Any(o => o.ActorID != 0 && o.ActorID == a.ActorSNO)) &&
  601. Trinity.SkipAheadAreaCache.Any(o => o.Position.Distance2D(a.Position) >= ObjectDistance &&
  602. !foundObjects.Any(fo => fo != new Tuple<int, Vector3>(o.ActorSNO, o.Position))))
  603. .OrderBy(o => o.Distance)
  604. .FirstOrDefault();
  605.  
  606. if (actor != null && actor.IsValid)
  607. return actor;
  608.  
  609. return default(DiaObject);
  610. }
  611. }
  612.  
  613. private Composite InvestigateActor()
  614. {
  615. return new Action(ret =>
  616. {
  617. RecordPosition();
  618.  
  619. var actor = ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false).FirstOrDefault(a => a.ActorSNO == ActorId);
  620.  
  621. if (actor != null && actor.IsValid && actor.Position.Distance2D(myPos) >= ObjectDistance)
  622. PlayerMover.NavigateTo(actor.Position);
  623. });
  624. }
  625.  
  626. private bool ShouldInvestigateActor()
  627. {
  628. if (ActorId == 0 || Objectives.All(o => o.ActorID == 0))
  629. return false;
  630.  
  631. var actors = ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false)
  632. .Where(a => (a.ActorSNO == ActorId ||
  633. Objectives.Any(o => o.ActorID != 0 && o.ActorID == a.ActorSNO)) &&
  634. Trinity.SkipAheadAreaCache.Any(o => o.Position.Distance2D(a.Position) >= ObjectDistance &&
  635. !foundObjects.Any(fo => fo != new Tuple<int, Vector3>(o.ActorSNO, o.Position))));
  636.  
  637. if (actors == null)
  638. return false;
  639.  
  640. if (!actors.Any())
  641. return false;
  642.  
  643. var actor = actors.OrderBy(a => a.Distance).FirstOrDefault();
  644.  
  645. if (actor.Distance <= ObjectDistance)
  646. return false;
  647.  
  648. return true;
  649. }
  650.  
  651.  
  652. /// <summary>
  653. /// Updates the search grid provider as needed
  654. /// </summary>
  655. /// <returns></returns>
  656. private Composite UpdateSearchGridProvider()
  657. {
  658. return
  659. new DecoratorContinue(ret => mySceneId != Trinity.Player.SceneId || Vector3.Distance(myPos, GPUpdatePosition) > 150,
  660. new Sequence(
  661. new Action(ret => mySceneId = Trinity.Player.SceneId),
  662. new Action(ret => Navigator.SearchGridProvider.Update()),
  663. new Action(ret => GPUpdatePosition = myPos),
  664. new Action(ret => MiniMapMarker.UpdateFailedMarkers())
  665. )
  666. );
  667. }
  668.  
  669. /// <summary>
  670. /// Checks if we are using a timeout and will end the tag if the timer has breached the given value
  671. /// </summary>
  672. /// <returns></returns>
  673. private Composite TimeoutCheck()
  674. {
  675. return
  676. new PrioritySelector(
  677. new Decorator(ret => timeoutBreached,
  678. new Sequence(
  679. new DecoratorContinue(ret => TownPortalOnTimeout && !Trinity.Player.IsInTown,
  680. new Sequence(
  681. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation,
  682. "TrinityExploreDungeon inactivity timer tripped ({0}), tag Using Town Portal!", TimeoutValue)),
  683. Zeta.Bot.CommonBehaviors.CreateUseTownPortal(),
  684. new Action(ret => isDone = true)
  685. )
  686. ),
  687. new DecoratorContinue(ret => !TownPortalOnTimeout,
  688. new Action(ret => isDone = true)
  689. )
  690. )
  691. ),
  692. new Decorator(ret => ExploreTimeoutType == TimeoutType.Timer,
  693. new Action(ret => CheckSetTimer(ret))
  694. ),
  695. new Decorator(ret => ExploreTimeoutType == TimeoutType.GoldInactivity,
  696. new Action(ret => CheckSetGoldInactive(ret))
  697. )
  698. );
  699. }
  700.  
  701. bool timeoutBreached = false;
  702. Stopwatch TagTimer = new Stopwatch();
  703. /// <summary>
  704. /// Will start the timer if needed, and end the tag if the timer has exceeded the TimeoutValue
  705. /// </summary>
  706. /// <param name="ctx"></param>
  707. /// <returns></returns>
  708. private RunStatus CheckSetTimer(object ctx)
  709. {
  710. if (!TagTimer.IsRunning)
  711. {
  712. TagTimer.Start();
  713. return RunStatus.Failure;
  714. }
  715. if (ExploreTimeoutType == TimeoutType.Timer && TagTimer.Elapsed.TotalSeconds > TimeoutValue)
  716. {
  717. Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "TrinityExploreDungeon timer ended ({0}), tag finished!", TimeoutValue);
  718. timeoutBreached = true;
  719. return RunStatus.Success;
  720. }
  721. return RunStatus.Failure;
  722. }
  723.  
  724. private int lastCoinage = -1;
  725. /// <summary>
  726. /// Will check if the bot has not picked up any gold within the allocated TimeoutValue
  727. /// </summary>
  728. /// <param name="ctx"></param>
  729. /// <returns></returns>
  730. private RunStatus CheckSetGoldInactive(object ctx)
  731. {
  732. CheckSetTimer(ctx);
  733. if (lastCoinage == -1)
  734. {
  735. lastCoinage = Trinity.Player.Coinage;
  736. return RunStatus.Failure;
  737. }
  738. else if (lastCoinage != Trinity.Player.Coinage)
  739. {
  740. TagTimer.Restart();
  741. return RunStatus.Failure;
  742. }
  743. else if (lastCoinage == Trinity.Player.Coinage && TagTimer.Elapsed.TotalSeconds > TimeoutValue)
  744. {
  745. Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "TrinityExploreDungeon gold inactivity timer tripped ({0}), tag finished!", TimeoutValue);
  746. timeoutBreached = true;
  747. return RunStatus.Success;
  748. }
  749.  
  750. return RunStatus.Failure;
  751. }
  752.  
  753. private int timesForcedReset = 0;
  754. private int timesForceResetMax = 5;
  755.  
  756. /// <summary>
  757. /// Checks to see if the tag is finished as needed
  758. /// </summary>
  759. /// <returns></returns>
  760. private Composite CheckIsExplorerFinished()
  761. {
  762. return
  763. new PrioritySelector(
  764. CheckIsObjectiveFinished(),
  765. new Decorator(ret => GetRouteUnvisitedNodeCount() == 0 && timesForcedReset > timesForceResetMax,
  766. new Sequence(
  767. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation,
  768. "Visited all nodes but objective not complete, forced reset more than {0} times, finished!", timesForceResetMax)),
  769. new Action(ret => isDone = true)
  770. )
  771. ),
  772. new Decorator(ret => GetRouteUnvisitedNodeCount() == 0,
  773. new Sequence(
  774. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Visited all nodes but objective not complete, forcing grid reset!")),
  775. new DecoratorContinue(ret => timesForcedReset > 2 && GetCurrentRouteNodeCount() == 1,
  776. new Sequence(
  777. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Only 1 node found and 3 grid resets, falling back to failsafe!")),
  778. new Action(ret => BoxSize = 25),
  779. new Action(ret => BoxTolerance = 0.01f),
  780. new Action(ret => IgnoreScenes.Clear())
  781. )
  782. ),
  783. new Action(ret => timesForcedReset++),
  784. new Action(ret => Trinity.SkipAheadAreaCache.Clear()),
  785. new Action(ret => MiniMapMarker.KnownMarkers.Clear()),
  786. new Action(ret => ForceUpdateScenes()),
  787. new Action(ret => GridSegmentation.Reset()),
  788. new Action(ret => GridSegmentation.Update()),
  789. new Action(ret => BrainBehavior.DungeonExplorer.Reset()),
  790. new Action(ret => PriorityScenesInvestigated.Clear()),
  791. new Action(ret => UpdateRoute())
  792. )
  793. )
  794. );
  795. }
  796.  
  797. private void ForceUpdateScenes()
  798. {
  799. foreach (Scene scene in ZetaDia.Scenes.GetScenes().ToList())
  800. {
  801. scene.UpdatePointer(scene.BaseAddress);
  802. }
  803. }
  804.  
  805. /// <summary>
  806. /// Checks to see if the tag is finished as needed
  807. /// </summary>
  808. /// <returns></returns>
  809. private Composite CheckIsObjectiveFinished()
  810. {
  811. return
  812. new PrioritySelector(
  813. TimeoutCheck(),
  814. new Decorator(ret => !StayAfterBounty && GetIsBountyDone(),
  815. new Sequence(
  816. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Bounty is done. Tag Finished.", IgnoreLastNodes)),
  817. new Action(ret => isDone = true)
  818. )
  819. ),
  820. new Decorator(ret => EndType == TrinityExploreEndType.FullyExplored && IgnoreLastNodes > 0 && GetRouteUnvisitedNodeCount() <= IgnoreLastNodes && GetGridSegmentationVisistedNodeCount() >= MinVisistedNodes,
  821. new Sequence(
  822. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Fully explored area! Ignoring {0} nodes. Tag Finished.", IgnoreLastNodes)),
  823. new Action(ret => isDone = true)
  824. )
  825. ),
  826. new Decorator(ret => EndType == TrinityExploreEndType.FullyExplored && GetRouteUnvisitedNodeCount() == 0,
  827. new Sequence(
  828. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Fully explored area! Tag Finished.", 0)),
  829. new Action(ret => isDone = true)
  830. )
  831. ),
  832. new Decorator(ret => EndType == TrinityExploreEndType.ExitFound && ExitNameHash != 0 && IsExitNameHashVisible(),
  833. new Sequence(
  834. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found exitNameHash {0}!", ExitNameHash)),
  835. new Action(ret => isDone = true)
  836. )
  837. ),
  838. new Decorator(ret => (EndType == TrinityExploreEndType.ObjectFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && ActorId != 0 && ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false)
  839. .Any(a => a.ActorSNO == ActorId && a.Distance <= ObjectDistance),
  840. new Sequence(
  841. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found Object {0}!", ActorId)),
  842. new Action(ret => isDone = true)
  843. )
  844. ),
  845. new Decorator(ret => (EndType == TrinityExploreEndType.ObjectFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && AlternateActorsFound(),
  846. new Sequence(
  847. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found Alternate Object {0}!", GetAlternateActor().ActorSNO)),
  848. new Action(ret => isDone = true)
  849. )
  850. ),
  851. new Decorator(ret => EndType == TrinityExploreEndType.SceneFound && Trinity.Player.SceneId == SceneId,
  852. new Sequence(
  853. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found SceneId {0}!", SceneId)),
  854. new Action(ret => isDone = true)
  855. )
  856. ),
  857. new Decorator(ret => EndType == TrinityExploreEndType.SceneFound && !string.IsNullOrWhiteSpace(SceneName) && ZetaDia.Me.CurrentScene.Name.ToLower().Contains(SceneName.ToLower()),
  858. new Sequence(
  859. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found SceneName {0}!", SceneName)),
  860. new Action(ret => isDone = true)
  861. )
  862. ),
  863. new Decorator(ret => EndType == TrinityExploreEndType.SceneLeftOrActorFound && SceneId != 0 && SceneIdLeft(),
  864. new Sequence(
  865. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Left SceneId {0}!", SceneId)),
  866. new Action(ret => isDone = true)
  867. )
  868. ),
  869. new Decorator(ret => (EndType == TrinityExploreEndType.SceneFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && !string.IsNullOrWhiteSpace(SceneName) && SceneNameLeft(),
  870. new Sequence(
  871. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Left SceneName {0}!", SceneName)),
  872. new Action(ret => isDone = true)
  873. )
  874. ),
  875. new Decorator(ret => Trinity.Player.IsInTown,
  876. new Sequence(
  877. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Cannot use TrinityExploreDungeon in town - tag finished!", SceneName)),
  878. new Action(ret => isDone = true)
  879. )
  880. )
  881. );
  882. }
  883.  
  884. private bool AlternateActorsFound()
  885. {
  886. return AlternateActors.Any() && ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false)
  887. .Where(o => AlternateActors.Any(a => a.ActorId == o.ActorSNO && o.Distance <= a.ObjectDistance)).Any();
  888. }
  889.  
  890. private bool SceneIdLeft()
  891. {
  892. return Trinity.Player.SceneId != SceneId;
  893. }
  894.  
  895. private bool SceneNameLeft()
  896. {
  897. return !ZetaDia.Me.CurrentScene.Name.ToLower().Contains(SceneName.ToLower()) && AlternateScenes != null && AlternateScenes.Any() && AlternateScenes.All(o => !ZetaDia.Me.CurrentScene.Name.ToLower().Contains(o.SceneName.ToLower()));
  898. }
  899.  
  900. private DiaObject GetAlternateActor()
  901. {
  902. return ZetaDia.Actors.GetActorsOfType<DiaObject>(true, false)
  903. .Where(o => AlternateActors.Any(a => a.ActorId == o.ActorSNO && o.Distance <= a.ObjectDistance)).OrderBy(o => o.Distance).FirstOrDefault();
  904. }
  905.  
  906. /// <summary>
  907. /// Determine if the tag ExitNameHash is visible in the list of Current World Markers
  908. /// </summary>
  909. /// <returns></returns>
  910. private bool IsExitNameHashVisible()
  911. {
  912. return ZetaDia.Minimap.Markers.CurrentWorldMarkers.Any(m => m.NameHash == ExitNameHash && m.Position.Distance2D(myPos) <= MarkerDistance + 10f);
  913. }
  914.  
  915. private Vector3 PrioritySceneTarget = Vector3.Zero;
  916. private int PrioritySceneSNOId = -1;
  917. private Scene CurrentPriorityScene = null;
  918. private float PriorityScenePathPrecision = -1f;
  919. /// <summary>
  920. /// A list of Scene SNOId's that have already been investigated
  921. /// </summary>
  922. private List<int> PriorityScenesInvestigated = new List<int>();
  923.  
  924. private DateTime lastCheckedScenes = DateTime.MinValue;
  925. /// <summary>
  926. /// Will find and move to Prioritized Scene's based on Scene SNOId or Name
  927. /// </summary>
  928. /// <returns></returns>
  929. private Composite PrioritySceneCheck()
  930. {
  931. return
  932. new Decorator(ret => PriorityScenes != null && PriorityScenes.Any(),
  933. new Sequence(
  934. new DecoratorContinue(ret => DateTime.UtcNow.Subtract(lastCheckedScenes).TotalMilliseconds > 1000,
  935. new Sequence(
  936. new Action(ret => lastCheckedScenes = DateTime.UtcNow),
  937. new Action(ret => FindPrioritySceneTarget())
  938. )
  939. ),
  940. new Decorator(ret => PrioritySceneTarget != Vector3.Zero,
  941. new PrioritySelector(
  942. new Decorator(ret => PrioritySceneTarget.Distance2D(myPos) <= PriorityScenePathPrecision,
  943. new Sequence(
  944. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Successfully navigated to priority scene {0} {1} center {2} Distance {3:0}",
  945. CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos))),
  946. new Action(ret => PrioritySceneMoveToFinished())
  947. )
  948. ),
  949. new Action(ret => MoveToPriorityScene())
  950. )
  951. )
  952. )
  953. );
  954. }
  955.  
  956. /// <summary>
  957. /// Handles actual movement to the Priority Scene
  958. /// </summary>
  959. private void MoveToPriorityScene()
  960. {
  961. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Moving to Priority Scene {0} - {1} Center {2} Distance {3:0}",
  962. CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
  963.  
  964. MoveResult moveResult = PlayerMover.NavigateTo(PrioritySceneTarget);
  965.  
  966. if (moveResult == MoveResult.PathGenerationFailed || moveResult == MoveResult.ReachedDestination)
  967. {
  968. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Unable to navigate to Scene {0} - {1} Center {2} Distance {3:0}, cancelling!",
  969. CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
  970. PrioritySceneMoveToFinished();
  971. }
  972. }
  973.  
  974. /// <summary>
  975. /// Sets a priority scene as finished
  976. /// </summary>
  977. private void PrioritySceneMoveToFinished()
  978. {
  979. PriorityScenesInvestigated.Add(PrioritySceneSNOId);
  980. PrioritySceneSNOId = -1;
  981. PrioritySceneTarget = Vector3.Zero;
  982. UpdateRoute();
  983. }
  984.  
  985. /// <summary>
  986. /// Finds a navigable point in a priority scene
  987. /// </summary>
  988. private void FindPrioritySceneTarget()
  989. {
  990. if (!PriorityScenes.Any())
  991. return;
  992.  
  993. if (PrioritySceneTarget != Vector3.Zero)
  994. return;
  995.  
  996. bool foundPriorityScene = false;
  997.  
  998. // find any matching priority scenes in scene manager - match by name or SNOId
  999.  
  1000. List<Scene> PScenes = ZetaDia.Scenes.GetScenes()
  1001. .Where(s => PriorityScenes.Any(ps => ps.SceneId != -1 && s.SceneInfo.SNOId == ps.SceneId)).ToList();
  1002.  
  1003. PScenes.AddRange(ZetaDia.Scenes.GetScenes()
  1004. .Where(s => PriorityScenes.Any(ps => ps.SceneName.Trim() != String.Empty && ps.SceneId == -1 && s.Name.ToLower().Contains(ps.SceneName.ToLower()))).ToList());
  1005.  
  1006. List<Scene> foundPriorityScenes = new List<Scene>();
  1007. Dictionary<int, Vector3> foundPrioritySceneIndex = new Dictionary<int, Vector3>();
  1008.  
  1009. foreach (Scene scene in PScenes)
  1010. {
  1011. if (!scene.IsValid)
  1012. continue;
  1013. if (!scene.SceneInfo.IsValid)
  1014. continue;
  1015. if (!scene.Mesh.Zone.IsValid)
  1016. continue;
  1017. if (!scene.Mesh.Zone.NavZoneDef.IsValid)
  1018. continue;
  1019.  
  1020. if (PriorityScenesInvestigated.Contains(scene.SceneInfo.SNOId))
  1021. continue;
  1022.  
  1023. foundPriorityScene = true;
  1024.  
  1025. NavZone navZone = scene.Mesh.Zone;
  1026. NavZoneDef zoneDef = navZone.NavZoneDef;
  1027.  
  1028. Vector2 zoneMin = navZone.ZoneMin;
  1029. Vector2 zoneMax = navZone.ZoneMax;
  1030.  
  1031. Vector3 zoneCenter = GetNavZoneCenter(navZone);
  1032.  
  1033. List<NavCell> NavCells = zoneDef.NavCells.Where(c => c.IsValid && c.Flags.HasFlag(NavCellFlags.AllowWalk)).ToList();
  1034.  
  1035. if (!NavCells.Any())
  1036. continue;
  1037.  
  1038. NavCell bestCell = NavCells.OrderBy(c => GetNavCellCenter(c.Min, c.Max, navZone).Distance2D(zoneCenter)).FirstOrDefault();
  1039.  
  1040. if (bestCell != null && !foundPrioritySceneIndex.ContainsKey(scene.SceneInfo.SNOId))
  1041. {
  1042. foundPrioritySceneIndex.Add(scene.SceneInfo.SNOId, GetNavCellCenter(bestCell, navZone));
  1043. foundPriorityScenes.Add(scene);
  1044. }
  1045. else
  1046. {
  1047. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Found Priority Scene but could not find a navigable point!", true);
  1048. }
  1049. }
  1050.  
  1051. if (foundPrioritySceneIndex.Any())
  1052. {
  1053. KeyValuePair<int, Vector3> nearestPriorityScene = foundPrioritySceneIndex.OrderBy(s => s.Value.Distance2D(myPos)).FirstOrDefault();
  1054.  
  1055. PrioritySceneSNOId = nearestPriorityScene.Key;
  1056. PrioritySceneTarget = nearestPriorityScene.Value;
  1057. CurrentPriorityScene = foundPriorityScenes.FirstOrDefault(s => s.SceneInfo.SNOId == PrioritySceneSNOId);
  1058. PriorityScenePathPrecision = GetPriorityScenePathPrecision(PScenes.FirstOrDefault(s => s.SceneInfo.SNOId == nearestPriorityScene.Key));
  1059.  
  1060. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Found Priority Scene {0} - {1} Center {2} Distance {3:0}",
  1061. CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
  1062. }
  1063.  
  1064. if (!foundPriorityScene)
  1065. {
  1066. PrioritySceneTarget = Vector3.Zero;
  1067. }
  1068. }
  1069.  
  1070. private float GetPriorityScenePathPrecision(Scene scene)
  1071. {
  1072. return PriorityScenes.FirstOrDefault(ps => ps.SceneId != 0 && ps.SceneId == scene.SceneInfo.SNOId || scene.Name.ToLower().Contains(ps.SceneName.ToLower())).PathPrecision;
  1073. }
  1074.  
  1075. /// <summary>
  1076. /// Gets the center of a given Navigation Zone
  1077. /// </summary>
  1078. /// <param name="zone"></param>
  1079. /// <returns></returns>
  1080. private Vector3 GetNavZoneCenter(NavZone zone)
  1081. {
  1082. float X = zone.ZoneMin.X + ((zone.ZoneMax.X - zone.ZoneMin.X) / 2);
  1083. float Y = zone.ZoneMin.Y + ((zone.ZoneMax.Y - zone.ZoneMin.Y) / 2);
  1084.  
  1085. return new Vector3(X, Y, 0);
  1086. }
  1087.  
  1088. /// <summary>
  1089. /// Gets the center of a given Navigation Cell
  1090. /// </summary>
  1091. /// <param name="cell"></param>
  1092. /// <param name="zone"></param>
  1093. /// <returns></returns>
  1094. private Vector3 GetNavCellCenter(NavCell cell, NavZone zone)
  1095. {
  1096. return GetNavCellCenter(cell.Min, cell.Max, zone);
  1097. }
  1098.  
  1099. /// <summary>
  1100. /// Gets the center of a given box with min/max, adjusted for the Navigation Zone
  1101. /// </summary>
  1102. /// <param name="min"></param>
  1103. /// <param name="max"></param>
  1104. /// <param name="zone"></param>
  1105. /// <returns></returns>
  1106. private Vector3 GetNavCellCenter(Vector3 min, Vector3 max, NavZone zone)
  1107. {
  1108. float X = zone.ZoneMin.X + min.X + ((max.X - min.X) / 2);
  1109. float Y = zone.ZoneMin.Y + min.Y + ((max.Y - min.Y) / 2);
  1110. float Z = min.Z + ((max.Z - min.Z) / 2);
  1111.  
  1112. return new Vector3(X, Y, Z);
  1113. }
  1114.  
  1115. /// <summary>
  1116. /// Checks to see if the current DungeonExplorer node is in an Ignored scene, and marks the node immediately visited if so
  1117. /// </summary>
  1118. /// <returns></returns>
  1119. private Composite CheckIgnoredScenes()
  1120. {
  1121. return
  1122. new Decorator(ret => timesForcedReset == 0 && IgnoreScenes != null && IgnoreScenes.Any(),
  1123. new PrioritySelector(
  1124. new Decorator(ret => IsPositionInsideIgnoredScene(CurrentNavTarget),
  1125. new Sequence(
  1126. new Action(ret => SetNodeVisited("Node is in Ignored Scene"))
  1127. )
  1128. )
  1129. )
  1130. );
  1131. }
  1132.  
  1133.  
  1134. private bool IsPositionInsideIgnoredScene(Vector3 position)
  1135. {
  1136. return IgnoredAreas.Any(a => a.IsPositionInside(position));
  1137. }
  1138.  
  1139. /// <summary>
  1140. /// Determines if a given Vector3 is in a provided IgnoreScene (if the scene is loaded)
  1141. /// </summary>
  1142. /// <param name="position"></param>
  1143. /// <returns></returns>
  1144. private bool PositionInsideIgnoredScene(Vector3 position)
  1145. {
  1146. List<Scene> ignoredScenes = ZetaDia.Scenes.GetScenes()
  1147. .Where(scn => scn.IsValid && (IgnoreScenes.Any(igscn => !string.IsNullOrWhiteSpace(igscn.SceneName) && scn.Name.ToLower().Contains(igscn.SceneName.ToLower())) ||
  1148. IgnoreScenes.Any(igscn => scn.SceneInfo.SNOId == igscn.SceneId) &&
  1149. !PriorityScenes.Any(psc => !string.IsNullOrWhiteSpace(psc.SceneName) && scn.Name.ToLower().Contains(psc.SceneName)) &&
  1150. !PriorityScenes.Any(psc => psc.SceneId != -1 && scn.SceneInfo.SNOId != psc.SceneId))).ToList();
  1151.  
  1152. foreach (Scene scene in ignoredScenes)
  1153. {
  1154. if (scene.Mesh.Zone == null)
  1155. return true;
  1156.  
  1157. Vector2 pos = position.ToVector2();
  1158. Vector2 min = scene.Mesh.Zone.ZoneMin;
  1159. Vector2 max = scene.Mesh.Zone.ZoneMax;
  1160.  
  1161. if (pos.X >= min.X && pos.X <= max.X && pos.Y >= min.Y && pos.Y <= max.Y)
  1162. return true;
  1163. }
  1164. return false;
  1165. }
  1166.  
  1167. /// <summary>
  1168. /// Determines if the current node can be marked as Visited, and does so if needed
  1169. /// </summary>
  1170. /// <returns></returns>
  1171. private Composite CheckNodeFinished()
  1172. {
  1173. return
  1174. new PrioritySelector(
  1175. new Decorator(ret => LastMoveResult == MoveResult.ReachedDestination,
  1176. new Sequence(
  1177. new Action(ret => SetNodeVisited("Reached Destination")),
  1178. new Action(ret => LastMoveResult = MoveResult.Moved),
  1179. new Action(ret => UpdateRoute())
  1180. )
  1181. ),
  1182. new Decorator(ret => BrainBehavior.DungeonExplorer.CurrentNode.Visited,
  1183. new Sequence(
  1184. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Current node was already marked as visited!")),
  1185. new Action(ret => BrainBehavior.DungeonExplorer.CurrentRoute.Dequeue()),
  1186. new Action(ret => UpdateRoute())
  1187. )
  1188. ),
  1189. new Decorator(ret => GetRouteUnvisitedNodeCount() == 0 || !BrainBehavior.DungeonExplorer.CurrentRoute.Any(),
  1190. new Sequence(
  1191. new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Error - CheckIsNodeFinished() called while Route is empty!")),
  1192. new Action(ret => UpdateRoute())
  1193. )
  1194. ),
  1195. new Decorator(ret => CurrentNavTarget.Distance2D(myPos) <= PathPrecision,
  1196. new Sequence(
  1197. new Action(ret => SetNodeVisited(String.Format("Node {0} is within PathPrecision ({1:0}/{2:0})",
  1198. CurrentNavTarget, CurrentNavTarget.Distance2D(myPos), PathPrecision))),
  1199. new Action(ret => UpdateRoute())
  1200. )
  1201. ),
  1202. new Decorator(ret => CurrentNavTarget.Distance2D(myPos) <= 90f && !MainGridProvider.CanStandAt(MainGridProvider.WorldToGrid(CurrentNavTarget.ToVector2())),
  1203. new Sequence(
  1204. new Action(ret => SetNodeVisited("Center Not Navigable")),
  1205. new Action(ret => UpdateRoute())
  1206. )
  1207. ),
  1208. new Decorator(ret => CacheData.NavigationObstacles.Any(o => o.Position.Distance2D(CurrentNavTarget) <= o.Radius * 2),
  1209. new Sequence(
  1210. new Action(ret => SetNodeVisited("Navigation obstacle detected at node point")),
  1211. new Action(ret => UpdateRoute())
  1212. )
  1213. ),
  1214. //new Decorator(ret => PlayerMover.MovementSpeed == 0 && myPos.Distance2D(CurrentNavTarget) <= 50f && !Navigator.Raycast(myPos, CurrentNavTarget),
  1215. // new Sequence(
  1216. // new Action(ret => SetNodeVisited("Stuck moving to node point, marking done (in LoS and nearby!)")),
  1217. // new Action(ret => UpdateRoute())
  1218. // )
  1219. //),
  1220. new Decorator(ret => Trinity.SkipAheadAreaCache.Any(p => p.Position.Distance2D(CurrentNavTarget) <= PathPrecision),
  1221. new Sequence(
  1222. new Action(ret => SetNodeVisited("Found node to be in skip ahead cache, marking done")),
  1223. new Action(ret => UpdateRoute())
  1224. )
  1225. ),
  1226. CheckIgnoredScenes()
  1227. );
  1228. }
  1229.  
  1230. /// <summary>
  1231. /// Updates the DungeonExplorer Route
  1232. /// </summary>
  1233. private void UpdateRoute()
  1234. {
  1235. CheckResetDungeonExplorer();
  1236.  
  1237. BrainBehavior.DungeonExplorer.Update();
  1238. PrintNodeCounts("BrainBehavior.DungeonExplorer.Update");
  1239.  
  1240. // Throw an exception if this shiz don't work
  1241. ValidateCurrentRoute();
  1242. }
  1243.  
  1244. /// <summary>
  1245. /// Marks the current dungeon Explorer as Visited and dequeues it from the route
  1246. /// </summary>
  1247. /// <param name="reason"></param>
  1248. private void SetNodeVisited(string reason = "")
  1249. {
  1250. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Dequeueing current node {0} - {1}", BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter, reason);
  1251. BrainBehavior.DungeonExplorer.CurrentNode.Visited = true;
  1252. BrainBehavior.DungeonExplorer.CurrentRoute.Dequeue();
  1253.  
  1254. MarkNearbyNodesVisited();
  1255.  
  1256. PrintNodeCounts("SetNodeVisited");
  1257. }
  1258.  
  1259. public void MarkNearbyNodesVisited()
  1260. {
  1261. foreach (DungeonNode node in GridSegmentation.Nodes.Where(n => !n.Visited))
  1262. {
  1263. float distance = node.NavigableCenter.Distance2D(myPos);
  1264. if (distance <= PathPrecision)
  1265. {
  1266. node.Visited = true;
  1267. string reason2 = String.Format("Node {0} is within path precision {1:0}/{2:0}", node.NavigableCenter, distance, PathPrecision);
  1268. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Marking unvisited nearby node as visited - {0}", reason2);
  1269. }
  1270. }
  1271. }
  1272.  
  1273. /// <summary>
  1274. /// Makes sure the current route is not null! Bad stuff happens if it's null...
  1275. /// </summary>
  1276. private static void ValidateCurrentRoute()
  1277. {
  1278. if (BrainBehavior.DungeonExplorer.CurrentRoute == null)
  1279. {
  1280. throw new ApplicationException("DungeonExplorer CurrentRoute is null");
  1281. }
  1282. }
  1283.  
  1284. /// <summary>
  1285. /// Prints a plethora of useful information about the Dungeon Exploration process
  1286. /// </summary>
  1287. /// <param name="step"></param>
  1288. private void PrintNodeCounts(string step = "")
  1289. {
  1290. if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.ProfileTag))
  1291. {
  1292. string nodeDistance = String.Empty;
  1293. if (GetRouteUnvisitedNodeCount() > 0)
  1294. {
  1295. try
  1296. {
  1297. float distance = BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter.Distance(myPos);
  1298.  
  1299. if (distance > 0)
  1300. nodeDistance = String.Format("Dist:{0:0}", Math.Round(distance / 10f, 2) * 10f);
  1301. }
  1302. catch { }
  1303. }
  1304.  
  1305. var log = String.Format("Nodes [Unvisited: Route:{1} Grid:{3} | Grid-Visited: {2}] Box:{4}/{5} Step:{6} {7} Nav:{8} RayCast:{9} PP:{10:0} Dir: {11} ZDiff:{12:0}",
  1306. GetRouteVisistedNodeCount(), // 0
  1307. GetRouteUnvisitedNodeCount(), // 1
  1308. GetGridSegmentationVisistedNodeCount(), // 2
  1309. GetGridSegmentationUnvisitedNodeCount(), // 3
  1310. GridSegmentation.BoxSize, // 4
  1311. GridSegmentation.BoxTolerance, // 5
  1312. step, // 6
  1313. nodeDistance, // 7
  1314. MainGridProvider.CanStandAt(MainGridProvider.WorldToGrid(CurrentNavTarget.ToVector2())), // 8
  1315. !Navigator.Raycast(myPos, CurrentNavTarget),
  1316. PathPrecision,
  1317. MathUtil.GetHeadingToPoint(CurrentNavTarget),
  1318. Math.Abs(myPos.Z - CurrentNavTarget.Z)
  1319. );
  1320.  
  1321. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, log);
  1322. }
  1323. }
  1324.  
  1325. /*
  1326. * Dungeon Explorer Nodes
  1327. */
  1328. /// <summary>
  1329. /// Gets the number of unvisited nodes in the DungeonExplorer Route
  1330. /// </summary>
  1331. /// <returns></returns>
  1332. private int GetRouteUnvisitedNodeCount()
  1333. {
  1334. if (GetCurrentRouteNodeCount() > 0)
  1335. return BrainBehavior.DungeonExplorer.CurrentRoute.Count(n => !n.Visited);
  1336. else
  1337. return 0;
  1338. }
  1339.  
  1340. /// <summary>
  1341. /// Gets the number of visisted nodes in the DungeonExplorer Route
  1342. /// </summary>
  1343. /// <returns></returns>
  1344. private int GetRouteVisistedNodeCount()
  1345. {
  1346. if (GetCurrentRouteNodeCount() > 0)
  1347. return BrainBehavior.DungeonExplorer.CurrentRoute.Count(n => n.Visited);
  1348. else
  1349. return 0;
  1350. }
  1351.  
  1352. /// <summary>
  1353. /// Gets the number of nodes in the DungeonExplorer Route
  1354. /// </summary>
  1355. /// <returns></returns>
  1356. private int GetCurrentRouteNodeCount()
  1357. {
  1358. if (BrainBehavior.DungeonExplorer.CurrentRoute != null)
  1359. return BrainBehavior.DungeonExplorer.CurrentRoute.Count();
  1360. else
  1361. return 0;
  1362. }
  1363. /*
  1364. * Grid Segmentation Nodes
  1365. */
  1366. /// <summary>
  1367. /// Gets the number of Unvisited nodes as reported by the Grid Segmentation provider
  1368. /// </summary>
  1369. /// <returns></returns>
  1370. private int GetGridSegmentationUnvisitedNodeCount()
  1371. {
  1372. if (GetGridSegmentationNodeCount() > 0)
  1373. return GridSegmentation.Nodes.Count(n => !n.Visited);
  1374. else
  1375. return 0;
  1376. }
  1377.  
  1378. /// <summary>
  1379. /// Gets the number of Visited nodes as reported by the Grid Segmentation provider
  1380. /// </summary>
  1381. /// <returns></returns>
  1382. private int GetGridSegmentationVisistedNodeCount()
  1383. {
  1384. if (GetCurrentRouteNodeCount() > 0)
  1385. return GridSegmentation.Nodes.Count(n => n.Visited);
  1386. else
  1387. return 0;
  1388. }
  1389.  
  1390. /// <summary>
  1391. /// Gets the total number of nodes with the current BoxSize/Tolerance as reported by the Grid Segmentation Provider
  1392. /// </summary>
  1393. /// <returns></returns>
  1394. private int GetGridSegmentationNodeCount()
  1395. {
  1396. if (GridSegmentation.Nodes != null)
  1397. return GridSegmentation.Nodes.Count();
  1398. else
  1399. return 0;
  1400. }
  1401.  
  1402. private MoveResult LastMoveResult = MoveResult.Moved;
  1403. private DateTime lastGeneratedPath = DateTime.MinValue;
  1404. /// <summary>
  1405. /// Moves the bot to the next DungeonExplorer node
  1406. /// </summary>
  1407. private void MoveToNextNode()
  1408. {
  1409. PlayerMover.RecordSkipAheadCachePoint();
  1410.  
  1411. NextNode = BrainBehavior.DungeonExplorer.CurrentNode;
  1412. Vector3 moveTarget = NextNode.NavigableCenter;
  1413.  
  1414. Vector3 lastPlayerMoverTarget = PlayerMover.LastMoveToTarget;
  1415. bool isStuck = DateTime.UtcNow.Subtract(PlayerMover.LastRecordedAnyStuck).TotalMilliseconds < 500;
  1416.  
  1417. if (isStuck && CacheData.NavigationObstacles.Any(o => MathUtil.IntersectsPath(o.Position, o.Radius, Trinity.Player.Position, lastPlayerMoverTarget)))
  1418. {
  1419. SetNodeVisited("Nav Obstacle detected!");
  1420. UpdateRoute();
  1421.  
  1422. return;
  1423. }
  1424.  
  1425. string nodeName = String.Format("{0} Distance: {1:0} Direction: {2}",
  1426. NextNode.NavigableCenter, NextNode.NavigableCenter.Distance(Trinity.Player.Position), MathUtil.GetHeadingToPoint(NextNode.NavigableCenter));
  1427.  
  1428. RecordPosition();
  1429.  
  1430. LastMoveResult = PlayerMover.NavigateTo(CurrentNavTarget);
  1431. //Navigator.MoveTo(CurrentNavTarget);
  1432. }
  1433.  
  1434. private void RecordPosition()
  1435. {
  1436. if (DateTime.UtcNow.Subtract(Trinity.lastAddedLocationCache).TotalMilliseconds >= 100)
  1437. {
  1438. Trinity.lastAddedLocationCache = DateTime.UtcNow;
  1439. if (Vector3.Distance(myPos, Trinity.LastRecordedPosition) >= 5f)
  1440. {
  1441. MarkNearbyNodesVisited();
  1442. Trinity.SkipAheadAreaCache.Add(new CacheObstacleObject(myPos, 20f, 0));
  1443. Trinity.LastRecordedPosition = myPos;
  1444. }
  1445. }
  1446. }
  1447. /// <summary>
  1448. /// Initizializes the profile tag and sets defaults as needed
  1449. /// </summary>
  1450. private void Init(bool forced = false)
  1451. {
  1452. if (BoxSize == 0)
  1453. BoxSize = 15;
  1454.  
  1455. if (BoxTolerance == 0)
  1456. BoxTolerance = 0.55f;
  1457.  
  1458. if (PathPrecision == 0)
  1459. PathPrecision = BoxSize / 2f;
  1460.  
  1461. float minPathPrecision = 15f;
  1462.  
  1463. if (PathPrecision < minPathPrecision)
  1464. PathPrecision = minPathPrecision;
  1465.  
  1466. if (ObjectDistance == 0)
  1467. ObjectDistance = 40f;
  1468.  
  1469. if (MarkerDistance == 0)
  1470. MarkerDistance = 25f;
  1471.  
  1472. if (TimeoutValue == 0)
  1473. TimeoutValue = 1800;
  1474.  
  1475. Trinity.SkipAheadAreaCache.Clear();
  1476. PriorityScenesInvestigated.Clear();
  1477. MiniMapMarker.KnownMarkers.Clear();
  1478. if (PriorityScenes == null)
  1479. PriorityScenes = new List<PrioritizeScene>();
  1480.  
  1481. if (IgnoreScenes == null)
  1482. IgnoreScenes = new List<IgnoreScene>();
  1483.  
  1484. if (AlternateActors == null)
  1485. AlternateActors = new List<AlternateActor>();
  1486.  
  1487. if (!forced)
  1488. {
  1489. Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag,
  1490. "Initialized TrinityExploreDungeon: boxSize={0} boxTolerance={1:0.00} endType={2} timeoutType={3} timeoutValue={4} pathPrecision={5:0} sceneId={6} actorId={7} objectDistance={8} markerDistance={9} exitNameHash={10}",
  1491. GridSegmentation.BoxSize, GridSegmentation.BoxTolerance, EndType, ExploreTimeoutType, TimeoutValue, PathPrecision, SceneId, ActorId, ObjectDistance, MarkerDistance, ExitNameHash);
  1492. }
  1493. InitDone = true;
  1494. }
  1495.  
  1496.  
  1497. private bool isDone = false;
  1498. /// <summary>
  1499. /// When true, the next profile tag is used
  1500. /// </summary>
  1501. public override bool IsDone
  1502. {
  1503. get { return !IsActiveQuestStep || isDone; }
  1504. }
  1505.  
  1506. /// <summary>
  1507. /// Resets this profile tag to defaults
  1508. /// </summary>
  1509. public override void ResetCachedDone()
  1510. {
  1511. isDone = false;
  1512. InitDone = false;
  1513. }
  1514.  
  1515. public bool GetIsBountyDone()
  1516. {
  1517. try
  1518. {
  1519. // Only valid for Adventure mode
  1520. if (ZetaDia.CurrentAct != Act.OpenWorld)
  1521. return false;
  1522.  
  1523. // We're in a rift, not a bounty!
  1524. if (ZetaDia.CurrentAct == Act.OpenWorld && DataDictionary.RiftWorldIds.Contains(ZetaDia.CurrentWorldId))
  1525. return false;
  1526.  
  1527. if (ZetaDia.IsInTown)
  1528. {
  1529. return true;
  1530. }
  1531. if (ZetaDia.Me.IsInBossEncounter)
  1532. {
  1533. return false;
  1534. }
  1535.  
  1536. var b = ZetaDia.ActInfo.ActiveBounty;
  1537. if (b == null)
  1538. {
  1539. Logger.Log("Active bounty returned null, Assuming done.");
  1540. return true;
  1541. }
  1542. else
  1543. {
  1544. if (!b.Info.IsValid)
  1545. {
  1546. Logger.Log("Does this even work? Thinks the bounty is not valid.");
  1547. }
  1548. if (b.Info.QuestSNO > 500000 || b.Info.QuestSNO < 200000)
  1549. {
  1550. Logger.Log("Got some weird numbers going on with the QuestSNO of the active bounty. Assuming glitched and done.");
  1551. return true;
  1552. }
  1553. //If completed or on next step, we are good.
  1554. if (b.Info.State == QuestState.Completed)
  1555. {
  1556. Logger.Log("Seems completed!");
  1557. return true;
  1558. }
  1559. }
  1560.  
  1561. }
  1562. catch (Exception ex)
  1563. {
  1564. Logger.LogError("Exception reading ActiveBounty " + ex.Message);
  1565. }
  1566.  
  1567. return false;
  1568. }
  1569.  
  1570. }
  1571. }
  1572.  
  1573. /*
  1574. * Never need to call GridSegmentation.Update()
  1575. * GridSegmentation.Reset() is automatically called on world change
  1576. * DungeonExplorer.Reset() will reset the current route and revisit nodes
  1577. * DungeonExplorer.Update() will update the current route to include new scenes
  1578. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement