using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Trinity.DbProvider;
using Trinity.Technicals;
using Zeta.Bot.Dungeons;
using Zeta.Bot.Logic;
using Zeta.Bot.Navigation;
using Zeta.Bot.Pathfinding;
using Zeta.Bot.Profile;
using Zeta.Common;
using Zeta.Game;
using Zeta.Game.Internals;
using Zeta.Game.Internals.Actors;
using Zeta.Game.Internals.SNO;
using Zeta.TreeSharp;
using Zeta.XmlEngine;
using Action = Zeta.TreeSharp.Action;
using Logger = Trinity.Technicals.Logger;
namespace Trinity.XmlTags
{
///
/// TrinityExploreDungeon is fuly backwards compatible with the built-in Demonbuddy ExploreArea tag. It provides additional features such as:
/// Moving to investigate MiniMapMarker pings and the current ExitNameHash if provided and visible (mini map marker 0 and the current exitNameHash)
/// Moving to investigate Priority Scenes if provided (PrioritizeScenes subtags)
/// Ignoring DungeonExplorer nodes in certain scenes if provided (IgnoreScenes subtags)
/// Reduced backtracking (via pathPrecision attribute and combat skip ahead cache)
/// Multiple ActorId's for the ObjectFound end type (AlternateActors sub-tags)
///
[XmlElement("TrinityExploreDungeon")]
public class TrinityExploreDungeon : ProfileBehavior
{
///
/// The SNOId of the Actor that we're looking for, used with until="ObjectFound"
///
[XmlAttribute("actorId", true)]
public int ActorId { get; set; }
///
/// Sets a custom grid segmentation Box Size (default 15)
///
[XmlAttribute("boxSize", true)]
public int BoxSize { get; set; }
///
/// Sets a custom grid segmentation Box Tolerance (default 0.55)
///
[XmlAttribute("boxTolerance", true)]
public float BoxTolerance { get; set; }
///
/// The nameHash of the exit the bot will move to and finish the tag when found
///
[XmlAttribute("exitNameHash", true)]
public int ExitNameHash { get; set; }
[XmlAttribute("ignoreGridReset", true)]
public bool IgnoreGridReset { get; set; }
///
/// Not currently implimented
///
[XmlAttribute("leaveWhenFinished", true)]
public bool LeaveWhenExplored { get; set; }
///
/// Not currently implimented
///
[XmlAttribute("leaveAfterBounty", true)]
public bool LeaveAfterBounty { get; set; }
///
/// The distance the bot must be from an actor before marking the tag as complete, when used with until="ObjectFound"
///
[XmlAttribute("objectDistance", true)]
public float ObjectDistance { get; set; }
///
/// The until="" atribute must match one of these
///
public enum TrinityExploreEndType
{
FullyExplored = 0,
ObjectFound,
ExitFound,
SceneFound,
SceneLeftOrActorFound
}
[XmlAttribute("endType", true)]
[XmlAttribute("until", true)]
public TrinityExploreEndType EndType { get; set; }
///
/// The list of Scene SNOId's or Scene Names that the bot will ignore dungeon nodes in
///
[XmlElement("IgnoreScenes")]
public List IgnoreScenes { get; set; }
///
/// The list of Scene SNOId's or Scene Names that the bot will prioritize (only works when the scene is "loaded")
///
[XmlElement("PriorityScenes")]
[XmlElement("PrioritizeScenes")]
public List PriorityScenes { get; set; }
///
/// The list of Scene SNOId's or Scene Names that the bot will use for endtype SceneLeftOrActorFound
///
[XmlElement("AlternateScenes")]
public List AlternateScenes { get; set; }
///
/// The Ignore Scene class, used as IgnoreScenes child elements
///
[XmlElement("IgnoreScene")]
public class IgnoreScene : IEquatable
{
[XmlAttribute("sceneName")]
public string SceneName { get; set; }
[XmlAttribute("sceneId")]
public int SceneId { get; set; }
public IgnoreScene()
{
SceneId = -1;
SceneName = String.Empty;
}
public IgnoreScene(string name)
{
this.SceneName = name;
}
public IgnoreScene(int id)
{
this.SceneId = id;
}
public bool Equals(Scene other)
{
return (!string.IsNullOrWhiteSpace(SceneName) && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
}
}
private CachedValue> m_IgnoredAreas;
private List IgnoredAreas
{
get
{
if (m_IgnoredAreas == null)
m_IgnoredAreas = new CachedValue>(() => { return GetIgnoredAreas(); }, TimeSpan.FromSeconds(1));
return m_IgnoredAreas.Value;
}
}
private List GetIgnoredAreas()
{
var ignoredScenes = ZetaDia.Scenes.GetScenes()
.Where(scn => scn.IsValid && IgnoreScenes.Any(igns => igns.Equals(scn)) && !PriorityScenes.Any(psc => psc.Equals(scn)))
.Select(scn =>
scn.Mesh.Zone == null
? new Area(new Vector2(float.MinValue, float.MinValue), new Vector2(float.MaxValue, float.MaxValue))
: new Area(scn.Mesh.Zone.ZoneMin, scn.Mesh.Zone.ZoneMax))
.ToList();
Logger.Log(LogCategory.ProfileTag, "Returning {0} ignored areas", ignoredScenes.Count());
return ignoredScenes;
}
private class Area
{
public Vector2 Min { get; set; }
public Vector2 Max { get; set; }
///
/// Initializes a new instance of the Area class.
///
public Area(Vector2 min, Vector2 max)
{
Min = min;
Max = max;
}
public bool IsPositionInside(Vector2 position)
{
return position.X >= Min.X && position.X <= Max.X && position.Y >= Min.Y && position.Y <= Max.Y;
}
public bool IsPositionInside(Vector3 position)
{
return IsPositionInside(position.ToVector2());
}
}
///
/// The Priority Scene class, used as PrioritizeScenes child elements
///
[XmlElement("PriorityScene")]
[XmlElement("PrioritizeScene")]
public class PrioritizeScene : IEquatable
{
[XmlAttribute("sceneName")]
public string SceneName { get; set; }
[XmlAttribute("sceneId")]
public int SceneId { get; set; }
[XmlAttribute("pathPrecision")]
public float PathPrecision { get; set; }
public PrioritizeScene()
{
PathPrecision = 15f;
SceneName = String.Empty;
SceneId = -1;
}
public PrioritizeScene(string name)
{
this.SceneName = name;
}
public PrioritizeScene(int id)
{
this.SceneId = id;
}
public bool Equals(Scene other)
{
return (SceneName != String.Empty && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
}
}
///
/// The Alternate Scene class, used as AlternateScenes child elements
///
[XmlElement("AlternateScene")]
public class AlternateScene : IEquatable
{
[XmlAttribute("sceneName")]
public string SceneName { get; set; }
[XmlAttribute("sceneId")]
public int SceneId { get; set; }
[XmlAttribute("pathPrecision")]
public float PathPrecision { get; set; }
public AlternateScene()
{
PathPrecision = 15f;
SceneName = String.Empty;
SceneId = -1;
}
public AlternateScene(string name)
{
this.SceneName = name;
}
public AlternateScene(int id)
{
this.SceneId = id;
}
public bool Equals(Scene other)
{
return (SceneName != String.Empty && other.Name.ToLowerInvariant().Contains(SceneName.ToLowerInvariant())) || other.SceneInfo.SNOId == SceneId;
}
}
[XmlElement("AlternateActors")]
public List AlternateActors { get; set; }
[XmlElement("AlternateActor")]
public class AlternateActor
{
[XmlAttribute("actorId")]
public int ActorId { get; set; }
[XmlAttribute("objectDistance")]
public float ObjectDistance { get; set; }
[XmlAttribute("interactRange")]
public float InteractRange { get; set; }
public AlternateActor()
{
ActorId = -1;
ObjectDistance = 60f;
}
}
[XmlElement("Objectives")]
public List Objectives { get; set; }
[XmlElement("Objective")]
public class Objective
{
[XmlAttribute("actorId")]
public int ActorID { get; set; }
[XmlAttribute("markerNameHash")]
public int MarkerNameHash { get; set; }
[XmlAttribute("count")]
public int Count { get; set; }
[XmlAttribute("endAnimation")]
public SNOAnim EndAnimation { get; set; }
[XmlAttribute("interact")]
public bool Interact { get; set; }
public Objective()
{
}
}
///
/// The Scene SNOId, used with ExploreUntil="SceneFound"
///
[XmlAttribute("sceneId")]
public int SceneId { get; set; }
///
/// The Scene Name, used with ExploreUntil="SceneFound", a sub-string match will work
///
[XmlAttribute("sceneName")]
public string SceneName { get; set; }
///
/// The distance the bot will mark dungeon nodes as "visited" (default is 1/2 of box size, minimum 10)
///
[XmlAttribute("pathPrecision")]
public float PathPrecision { get; set; }
///
/// The distance before reaching a MiniMapMarker before marking it as visited
///
[XmlAttribute("markerDistance")]
public float MarkerDistance { get; set; }
///
/// Disable Mini Map Marker Scouting
///
[XmlAttribute("ignoreMarkers")]
public bool IgnoreMarkers { get; set; }
public enum TimeoutType
{
Timer,
GoldInactivity,
None,
}
///
/// The TimeoutType to use (default None, no timeout)
///
[XmlAttribute("timeoutType")]
public TimeoutType ExploreTimeoutType { get; set; }
///
/// Value in Seconds.
/// 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
///
[XmlAttribute("timeoutValue")]
public int TimeoutValue { get; set; }
///
/// If we want to use a townportal before ending the tag when a timeout happens
///
[XmlAttribute("townPortalOnTimeout")]
public bool TownPortalOnTimeout { get; set; }
///
/// Ignore last N nodes of dungeon explorer, when using endType=FullyExplored
///
[XmlAttribute("ignoreLastNodes")]
public int IgnoreLastNodes { get; set; }
///
/// Used with IgnoreLastNodes, minimum visited node count before tag can end.
/// The minVisistedNodes is purely, and only for use with ignoreLastNodes - it does not serve any other function like you expect.
/// The reason this attribute exists, is to prevent prematurely exiting the dungeon exploration when used with ignoreLastNodes.
/// 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,
/// the bot would immediately exit from navigation without exploring anything at all.
///
[XmlAttribute("minVisitedNodes")]
public int MinVisistedNodes { get; set; }
[XmlAttribute("SetNodesExploredAutomatically")]
[XmlAttribute("setNodesExploredAutomatically")]
public bool SetNodesExploredAutomatically { get; set; }
[XmlAttribute("minObjectOccurances")]
public int MinOccurances { get; set; }
[XmlAttribute("interactWithObject")]
public bool InteractWithObject { get; set; }
[XmlAttribute("interactRange")]
public float ObjectInteractRange { get; set; }
HashSet> foundObjects = new HashSet>();
///
/// The Position of the CurrentNode NavigableCenter
///
private Vector3 CurrentNavTarget
{
get
{
if (PrioritySceneTarget != Vector3.Zero)
{
return PrioritySceneTarget;
}
if (GetRouteUnvisitedNodeCount() > 0)
{
return BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter;
}
else
{
return Vector3.Zero;
}
}
}
// Adding these for SimpleFollow compatability
public float X { get { return CurrentNavTarget.X; } }
public float Y { get { return CurrentNavTarget.Y; } }
public float Z { get { return CurrentNavTarget.Z; } }
private bool InitDone = false;
private DungeonNode NextNode;
///
/// The current player position
///
private Vector3 myPos { get { return Trinity.Player.Position; } }
private static ISearchAreaProvider MainGridProvider
{
get
{
return Trinity.MainGridProvider;
}
}
///
/// The last scene SNOId we entered
///
private int mySceneId = -1;
///
/// The last position we updated the ISearchGridProvider at
///
private Vector3 GPUpdatePosition = Vector3.Zero;
///
/// Called when the profile behavior starts
///
public override void OnStart()
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "TrinityExploreDungeon OnStart() called");
if (SetNodesExploredAutomatically)
{
Logger.Log(LogCategory.ProfileTag, "Minimap Explored Nodes Enabled");
BrainBehavior.DungeonExplorer.SetNodesExploredAutomatically = true;
}
else
{
Logger.Log(LogCategory.ProfileTag, "Minimap Explored Nodes Disabled");
BrainBehavior.DungeonExplorer.SetNodesExploredAutomatically = false;
}
if (!IgnoreGridReset)
{
UpdateSearchGridProvider();
CheckResetDungeonExplorer();
GridSegmentation.Reset();
BrainBehavior.DungeonExplorer.Reset();
MiniMapMarker.KnownMarkers.Clear();
}
if (!InitDone)
{
Init();
}
TagTimer.Reset();
timesForcedReset = 0;
if (Objectives == null)
Objectives = new List();
if (ObjectDistance == 0)
ObjectDistance = 25f;
PrintNodeCounts("PostInit");
}
///
/// Re-sets the DungeonExplorer, BoxSize, BoxTolerance, and Updates the current route
///
private void CheckResetDungeonExplorer()
{
if (!ZetaDia.IsInGame || ZetaDia.IsLoadingWorld || !ZetaDia.WorldInfo.IsValid || !ZetaDia.Scenes.IsValid || !ZetaDia.Service.IsValid)
return;
// I added this because GridSegmentation may (rarely) reset itself without us doing it to 15/.55.
if ((BoxSize != 0 && BoxTolerance != 0) && (GridSegmentation.BoxSize != BoxSize || GridSegmentation.BoxTolerance != BoxTolerance) || (GetGridSegmentationNodeCount() == 0))
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Box Size or Tolerance has been changed! {0}/{1}", GridSegmentation.BoxSize, GridSegmentation.BoxTolerance);
BrainBehavior.DungeonExplorer.Reset();
PrintNodeCounts("BrainBehavior.DungeonExplorer.Reset");
GridSegmentation.BoxSize = BoxSize;
GridSegmentation.BoxTolerance = BoxTolerance;
PrintNodeCounts("SetBoxSize+Tolerance");
BrainBehavior.DungeonExplorer.Update();
PrintNodeCounts("BrainBehavior.DungeonExplorer.Update");
}
}
///
/// The main profile behavior
///
///
protected override Composite CreateBehavior()
{
return
new Sequence(
new DecoratorContinue(ret => !IgnoreMarkers,
new Sequence(
MiniMapMarker.DetectMiniMapMarkers(0),
MiniMapMarker.DetectMiniMapMarkers(ExitNameHash),
MiniMapMarker.DetectMiniMapMarkers(Objectives)
)
),
UpdateSearchGridProvider(),
new Action(ret => CheckResetDungeonExplorer()),
new PrioritySelector(
CheckIsObjectiveFinished(),
PrioritySceneCheck(),
new Decorator(ret => !IgnoreMarkers,
MiniMapMarker.VisitMiniMapMarkers(myPos, MarkerDistance)
),
new Decorator(ret => ShouldInvestigateActor(),
new PrioritySelector(
new Decorator(ret => CurrentActor != null && CurrentActor.IsValid &&
Objectives.Any(o => o.ActorID == CurrentActor.ActorSNO && o.Interact) &&
CurrentActor.Position.Distance(Trinity.Player.Position) <= CurrentActor.CollisionSphere.Radius,
new Sequence(
new Action(ret => CurrentActor.Interact())
)
),
InvestigateActor()
)
),
new Sequence(
new DecoratorContinue(ret => DungeonRouteIsEmpty(),
new Action(ret => UpdateRoute())
),
CheckIsExplorerFinished()
),
new DecoratorContinue(ret => DungeonRouteIsValid(),
new PrioritySelector(
CheckNodeFinished(),
new Sequence(
new Action(ret => PrintNodeCounts("MainBehavior")),
new Action(ret => MoveToNextNode())
)
)
),
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Error 1: Unknown error occured!"))
)
);
}
private static bool DungeonRouteIsValid()
{
return BrainBehavior.DungeonExplorer != null && BrainBehavior.DungeonExplorer.CurrentRoute != null && BrainBehavior.DungeonExplorer.CurrentRoute.Any();
}
private static bool DungeonRouteIsEmpty()
{
return BrainBehavior.DungeonExplorer != null && BrainBehavior.DungeonExplorer.CurrentRoute != null && !BrainBehavior.DungeonExplorer.CurrentRoute.Any();
}
private bool CurrentActorIsFinished
{
get
{
return Objectives.Any(o => o.ActorID == CurrentActor.ActorSNO && o.EndAnimation == CurrentActor.CommonData.CurrentAnimation);
}
}
private DiaObject CurrentActor
{
get
{
var actor =
ZetaDia.Actors.GetActorsOfType(true, false)
.Where(a => (a.ActorSNO == ActorId ||
Objectives.Any(o => o.ActorID != 0 && o.ActorID == a.ActorSNO)) &&
Trinity.SkipAheadAreaCache.Any(o => o.Position.Distance2D(a.Position) >= ObjectDistance &&
!foundObjects.Any(fo => fo != new Tuple(o.ActorSNO, o.Position))))
.OrderBy(o => o.Distance)
.FirstOrDefault();
if (actor != null && actor.IsValid)
return actor;
return default(DiaObject);
}
}
private Composite InvestigateActor()
{
return new Action(ret =>
{
RecordPosition();
var actor = ZetaDia.Actors.GetActorsOfType(true, false).FirstOrDefault(a => a.ActorSNO == ActorId);
if (actor != null && actor.IsValid && actor.Position.Distance2D(myPos) >= ObjectDistance)
PlayerMover.NavigateTo(actor.Position);
});
}
private bool ShouldInvestigateActor()
{
if (ActorId == 0 || Objectives.All(o => o.ActorID == 0))
return false;
var actors = ZetaDia.Actors.GetActorsOfType(true, false)
.Where(a => (a.ActorSNO == ActorId ||
Objectives.Any(o => o.ActorID != 0 && o.ActorID == a.ActorSNO)) &&
Trinity.SkipAheadAreaCache.Any(o => o.Position.Distance2D(a.Position) >= ObjectDistance &&
!foundObjects.Any(fo => fo != new Tuple(o.ActorSNO, o.Position))));
if (actors == null)
return false;
if (!actors.Any())
return false;
var actor = actors.OrderBy(a => a.Distance).FirstOrDefault();
if (actor.Distance <= ObjectDistance)
return false;
return true;
}
///
/// Updates the search grid provider as needed
///
///
private Composite UpdateSearchGridProvider()
{
return
new DecoratorContinue(ret => mySceneId != Trinity.Player.SceneId || Vector3.Distance(myPos, GPUpdatePosition) > 150,
new Sequence(
new Action(ret => mySceneId = Trinity.Player.SceneId),
new Action(ret => Navigator.SearchGridProvider.Update()),
new Action(ret => GPUpdatePosition = myPos),
new Action(ret => MiniMapMarker.UpdateFailedMarkers())
)
);
}
///
/// Checks if we are using a timeout and will end the tag if the timer has breached the given value
///
///
private Composite TimeoutCheck()
{
return
new PrioritySelector(
new Decorator(ret => timeoutBreached,
new Sequence(
new DecoratorContinue(ret => TownPortalOnTimeout && !Trinity.Player.IsInTown,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation,
"TrinityExploreDungeon inactivity timer tripped ({0}), tag Using Town Portal!", TimeoutValue)),
Zeta.Bot.CommonBehaviors.CreateUseTownPortal(),
new Action(ret => isDone = true)
)
),
new DecoratorContinue(ret => !TownPortalOnTimeout,
new Action(ret => isDone = true)
)
)
),
new Decorator(ret => ExploreTimeoutType == TimeoutType.Timer,
new Action(ret => CheckSetTimer(ret))
),
new Decorator(ret => ExploreTimeoutType == TimeoutType.GoldInactivity,
new Action(ret => CheckSetGoldInactive(ret))
)
);
}
bool timeoutBreached = false;
Stopwatch TagTimer = new Stopwatch();
///
/// Will start the timer if needed, and end the tag if the timer has exceeded the TimeoutValue
///
///
///
private RunStatus CheckSetTimer(object ctx)
{
if (!TagTimer.IsRunning)
{
TagTimer.Start();
return RunStatus.Failure;
}
if (ExploreTimeoutType == TimeoutType.Timer && TagTimer.Elapsed.TotalSeconds > TimeoutValue)
{
Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "TrinityExploreDungeon timer ended ({0}), tag finished!", TimeoutValue);
timeoutBreached = true;
return RunStatus.Success;
}
return RunStatus.Failure;
}
private int lastCoinage = -1;
///
/// Will check if the bot has not picked up any gold within the allocated TimeoutValue
///
///
///
private RunStatus CheckSetGoldInactive(object ctx)
{
CheckSetTimer(ctx);
if (lastCoinage == -1)
{
lastCoinage = Trinity.Player.Coinage;
return RunStatus.Failure;
}
else if (lastCoinage != Trinity.Player.Coinage)
{
TagTimer.Restart();
return RunStatus.Failure;
}
else if (lastCoinage == Trinity.Player.Coinage && TagTimer.Elapsed.TotalSeconds > TimeoutValue)
{
Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "TrinityExploreDungeon gold inactivity timer tripped ({0}), tag finished!", TimeoutValue);
timeoutBreached = true;
return RunStatus.Success;
}
return RunStatus.Failure;
}
private int timesForcedReset = 0;
private int timesForceResetMax = 5;
///
/// Checks to see if the tag is finished as needed
///
///
private Composite CheckIsExplorerFinished()
{
return
new PrioritySelector(
CheckIsObjectiveFinished(),
new Decorator(ret => GetRouteUnvisitedNodeCount() == 0 && timesForcedReset > timesForceResetMax,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation,
"Visited all nodes but objective not complete, forced reset more than {0} times, finished!", timesForceResetMax)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => GetRouteUnvisitedNodeCount() == 0,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Visited all nodes but objective not complete, forcing grid reset!")),
new Action(ret => timesForcedReset++),
new Action(ret => Trinity.SkipAheadAreaCache.Clear()),
new Action(ret => MiniMapMarker.KnownMarkers.Clear()),
new Action(ret => ForceUpdateScenes()),
new Action(ret => GridSegmentation.Reset()),
new Action(ret => GridSegmentation.Update()),
new Action(ret => BrainBehavior.DungeonExplorer.Reset()),
new Action(ret => PriorityScenesInvestigated.Clear()),
new Action(ret => UpdateRoute())
)
)
);
}
private void ForceUpdateScenes()
{
foreach (Scene scene in ZetaDia.Scenes.GetScenes().ToList())
{
scene.UpdatePointer(scene.BaseAddress);
}
}
///
/// Checks to see if the tag is finished as needed
///
///
private Composite CheckIsObjectiveFinished()
{
return
new PrioritySelector(
TimeoutCheck(),
new Decorator(ret => LeaveAfterBounty && GetIsBountyDone(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Bounty is done. Tag Finished.", IgnoreLastNodes)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.FullyExplored && IgnoreLastNodes > 0 && GetRouteUnvisitedNodeCount() <= IgnoreLastNodes && GetGridSegmentationVisistedNodeCount() >= MinVisistedNodes,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Fully explored area! Ignoring {0} nodes. Tag Finished.", IgnoreLastNodes)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.FullyExplored && GetRouteUnvisitedNodeCount() == 0,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Fully explored area! Tag Finished.", 0)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.ExitFound && ExitNameHash != 0 && IsExitNameHashVisible(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found exitNameHash {0}!", ExitNameHash)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => (EndType == TrinityExploreEndType.ObjectFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && ActorId != 0 && ZetaDia.Actors.GetActorsOfType(true, false)
.Any(a => a.ActorSNO == ActorId && a.Distance <= ObjectDistance),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found Object {0}!", ActorId)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => (EndType == TrinityExploreEndType.ObjectFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && AlternateActorsFound(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found Alternate Object {0}!", GetAlternateActor().ActorSNO)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.SceneFound && Trinity.Player.SceneId == SceneId,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found SceneId {0}!", SceneId)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.SceneFound && !string.IsNullOrWhiteSpace(SceneName) && ZetaDia.Me.CurrentScene.Name.ToLower().Contains(SceneName.ToLower()),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Found SceneName {0}!", SceneName)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => EndType == TrinityExploreEndType.SceneLeftOrActorFound && SceneId != 0 && SceneIdLeft(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Left SceneId {0}!", SceneId)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => (EndType == TrinityExploreEndType.SceneFound || EndType == TrinityExploreEndType.SceneLeftOrActorFound) && !string.IsNullOrWhiteSpace(SceneName) && SceneNameLeft(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Left SceneName {0}!", SceneName)),
new Action(ret => isDone = true)
)
),
new Decorator(ret => Trinity.Player.IsInTown,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Cannot use TrinityExploreDungeon in town - tag finished!", SceneName)),
new Action(ret => isDone = true)
)
)
);
}
private bool AlternateActorsFound()
{
return AlternateActors.Any() && ZetaDia.Actors.GetActorsOfType(true, false)
.Where(o => AlternateActors.Any(a => a.ActorId == o.ActorSNO && o.Distance <= a.ObjectDistance)).Any();
}
private bool SceneIdLeft()
{
return Trinity.Player.SceneId != SceneId;
}
private bool SceneNameLeft()
{
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()));
}
private DiaObject GetAlternateActor()
{
return ZetaDia.Actors.GetActorsOfType(true, false)
.Where(o => AlternateActors.Any(a => a.ActorId == o.ActorSNO && o.Distance <= a.ObjectDistance)).OrderBy(o => o.Distance).FirstOrDefault();
}
///
/// Determine if the tag ExitNameHash is visible in the list of Current World Markers
///
///
private bool IsExitNameHashVisible()
{
return ZetaDia.Minimap.Markers.CurrentWorldMarkers.Any(m => m.NameHash == ExitNameHash && m.Position.Distance2D(myPos) <= MarkerDistance + 10f);
}
private Vector3 PrioritySceneTarget = Vector3.Zero;
private int PrioritySceneSNOId = -1;
private Scene CurrentPriorityScene = null;
private float PriorityScenePathPrecision = -1f;
///
/// A list of Scene SNOId's that have already been investigated
///
private List PriorityScenesInvestigated = new List();
private DateTime lastCheckedScenes = DateTime.MinValue;
///
/// Will find and move to Prioritized Scene's based on Scene SNOId or Name
///
///
private Composite PrioritySceneCheck()
{
return
new Decorator(ret => PriorityScenes != null && PriorityScenes.Any(),
new Sequence(
new DecoratorContinue(ret => DateTime.UtcNow.Subtract(lastCheckedScenes).TotalMilliseconds > 1000,
new Sequence(
new Action(ret => lastCheckedScenes = DateTime.UtcNow),
new Action(ret => FindPrioritySceneTarget())
)
),
new Decorator(ret => PrioritySceneTarget != Vector3.Zero,
new PrioritySelector(
new Decorator(ret => PrioritySceneTarget.Distance2D(myPos) <= PriorityScenePathPrecision,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Successfully navigated to priority scene {0} {1} center {2} Distance {3:0}",
CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos))),
new Action(ret => PrioritySceneMoveToFinished())
)
),
new Action(ret => MoveToPriorityScene())
)
)
)
);
}
///
/// Handles actual movement to the Priority Scene
///
private void MoveToPriorityScene()
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Moving to Priority Scene {0} - {1} Center {2} Distance {3:0}",
CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
MoveResult moveResult = PlayerMover.NavigateTo(PrioritySceneTarget);
if (moveResult == MoveResult.PathGenerationFailed || moveResult == MoveResult.ReachedDestination)
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Unable to navigate to Scene {0} - {1} Center {2} Distance {3:0}, cancelling!",
CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
PrioritySceneMoveToFinished();
}
}
///
/// Sets a priority scene as finished
///
private void PrioritySceneMoveToFinished()
{
PriorityScenesInvestigated.Add(PrioritySceneSNOId);
PrioritySceneSNOId = -1;
PrioritySceneTarget = Vector3.Zero;
UpdateRoute();
}
///
/// Finds a navigable point in a priority scene
///
private void FindPrioritySceneTarget()
{
if (!PriorityScenes.Any())
return;
if (PrioritySceneTarget != Vector3.Zero)
return;
bool foundPriorityScene = false;
// find any matching priority scenes in scene manager - match by name or SNOId
List PScenes = ZetaDia.Scenes.GetScenes()
.Where(s => PriorityScenes.Any(ps => ps.SceneId != -1 && s.SceneInfo.SNOId == ps.SceneId)).ToList();
PScenes.AddRange(ZetaDia.Scenes.GetScenes()
.Where(s => PriorityScenes.Any(ps => ps.SceneName.Trim() != String.Empty && ps.SceneId == -1 && s.Name.ToLower().Contains(ps.SceneName.ToLower()))).ToList());
List foundPriorityScenes = new List();
Dictionary foundPrioritySceneIndex = new Dictionary();
foreach (Scene scene in PScenes)
{
if (!scene.IsValid)
continue;
if (!scene.SceneInfo.IsValid)
continue;
if (!scene.Mesh.Zone.IsValid)
continue;
if (!scene.Mesh.Zone.NavZoneDef.IsValid)
continue;
if (PriorityScenesInvestigated.Contains(scene.SceneInfo.SNOId))
continue;
foundPriorityScene = true;
NavZone navZone = scene.Mesh.Zone;
NavZoneDef zoneDef = navZone.NavZoneDef;
Vector2 zoneMin = navZone.ZoneMin;
Vector2 zoneMax = navZone.ZoneMax;
Vector3 zoneCenter = GetNavZoneCenter(navZone);
List NavCells = zoneDef.NavCells.Where(c => c.IsValid && c.Flags.HasFlag(NavCellFlags.AllowWalk)).ToList();
if (!NavCells.Any())
continue;
NavCell bestCell = NavCells.OrderBy(c => GetNavCellCenter(c.Min, c.Max, navZone).Distance2D(zoneCenter)).FirstOrDefault();
if (bestCell != null && !foundPrioritySceneIndex.ContainsKey(scene.SceneInfo.SNOId))
{
foundPrioritySceneIndex.Add(scene.SceneInfo.SNOId, GetNavCellCenter(bestCell, navZone));
foundPriorityScenes.Add(scene);
}
else
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Found Priority Scene but could not find a navigable point!", true);
}
}
if (foundPrioritySceneIndex.Any())
{
KeyValuePair nearestPriorityScene = foundPrioritySceneIndex.OrderBy(s => s.Value.Distance2D(myPos)).FirstOrDefault();
PrioritySceneSNOId = nearestPriorityScene.Key;
PrioritySceneTarget = nearestPriorityScene.Value;
CurrentPriorityScene = foundPriorityScenes.FirstOrDefault(s => s.SceneInfo.SNOId == PrioritySceneSNOId);
PriorityScenePathPrecision = GetPriorityScenePathPrecision(PScenes.FirstOrDefault(s => s.SceneInfo.SNOId == nearestPriorityScene.Key));
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Found Priority Scene {0} - {1} Center {2} Distance {3:0}",
CurrentPriorityScene.Name, CurrentPriorityScene.SceneInfo.SNOId, PrioritySceneTarget, PrioritySceneTarget.Distance2D(myPos));
}
if (!foundPriorityScene)
{
PrioritySceneTarget = Vector3.Zero;
}
}
private float GetPriorityScenePathPrecision(Scene scene)
{
return PriorityScenes.FirstOrDefault(ps => ps.SceneId != 0 && ps.SceneId == scene.SceneInfo.SNOId || scene.Name.ToLower().Contains(ps.SceneName.ToLower())).PathPrecision;
}
///
/// Gets the center of a given Navigation Zone
///
///
///
private Vector3 GetNavZoneCenter(NavZone zone)
{
float X = zone.ZoneMin.X + ((zone.ZoneMax.X - zone.ZoneMin.X) / 2);
float Y = zone.ZoneMin.Y + ((zone.ZoneMax.Y - zone.ZoneMin.Y) / 2);
return new Vector3(X, Y, 0);
}
///
/// Gets the center of a given Navigation Cell
///
///
///
///
private Vector3 GetNavCellCenter(NavCell cell, NavZone zone)
{
return GetNavCellCenter(cell.Min, cell.Max, zone);
}
///
/// Gets the center of a given box with min/max, adjusted for the Navigation Zone
///
///
///
///
///
private Vector3 GetNavCellCenter(Vector3 min, Vector3 max, NavZone zone)
{
float X = zone.ZoneMin.X + min.X + ((max.X - min.X) / 2);
float Y = zone.ZoneMin.Y + min.Y + ((max.Y - min.Y) / 2);
float Z = min.Z + ((max.Z - min.Z) / 2);
return new Vector3(X, Y, Z);
}
///
/// Checks to see if the current DungeonExplorer node is in an Ignored scene, and marks the node immediately visited if so
///
///
private Composite CheckIgnoredScenes()
{
return
new Decorator(ret => timesForcedReset == 0 && IgnoreScenes != null && IgnoreScenes.Any(),
new PrioritySelector(
new Decorator(ret => IsPositionInsideIgnoredScene(CurrentNavTarget),
new Sequence(
new Action(ret => SetNodeVisited("Node is in Ignored Scene"))
)
)
)
);
}
private bool IsPositionInsideIgnoredScene(Vector3 position)
{
return IgnoredAreas.Any(a => a.IsPositionInside(position));
}
///
/// Determines if a given Vector3 is in a provided IgnoreScene (if the scene is loaded)
///
///
///
private bool PositionInsideIgnoredScene(Vector3 position)
{
List ignoredScenes = ZetaDia.Scenes.GetScenes()
.Where(scn => scn.IsValid && (IgnoreScenes.Any(igscn => !string.IsNullOrWhiteSpace(igscn.SceneName) && scn.Name.ToLower().Contains(igscn.SceneName.ToLower())) ||
IgnoreScenes.Any(igscn => scn.SceneInfo.SNOId == igscn.SceneId) &&
!PriorityScenes.Any(psc => !string.IsNullOrWhiteSpace(psc.SceneName) && scn.Name.ToLower().Contains(psc.SceneName)) &&
!PriorityScenes.Any(psc => psc.SceneId != -1 && scn.SceneInfo.SNOId != psc.SceneId))).ToList();
foreach (Scene scene in ignoredScenes)
{
if (scene.Mesh.Zone == null)
return true;
Vector2 pos = position.ToVector2();
Vector2 min = scene.Mesh.Zone.ZoneMin;
Vector2 max = scene.Mesh.Zone.ZoneMax;
if (pos.X >= min.X && pos.X <= max.X && pos.Y >= min.Y && pos.Y <= max.Y)
return true;
}
return false;
}
///
/// Determines if the current node can be marked as Visited, and does so if needed
///
///
private Composite CheckNodeFinished()
{
return
new PrioritySelector(
new Decorator(ret => LastMoveResult == MoveResult.ReachedDestination,
new Sequence(
new Action(ret => SetNodeVisited("Reached Destination")),
new Action(ret => LastMoveResult = MoveResult.Moved),
new Action(ret => UpdateRoute())
)
),
new Decorator(ret => BrainBehavior.DungeonExplorer.CurrentNode.Visited,
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Current node was already marked as visited!")),
new Action(ret => BrainBehavior.DungeonExplorer.CurrentRoute.Dequeue()),
new Action(ret => UpdateRoute())
)
),
new Decorator(ret => GetRouteUnvisitedNodeCount() == 0 || !BrainBehavior.DungeonExplorer.CurrentRoute.Any(),
new Sequence(
new Action(ret => Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Error - CheckIsNodeFinished() called while Route is empty!")),
new Action(ret => UpdateRoute())
)
),
new Decorator(ret => CurrentNavTarget.Distance2D(myPos) <= PathPrecision,
new Sequence(
new Action(ret => SetNodeVisited(String.Format("Node {0} is within PathPrecision ({1:0}/{2:0})",
CurrentNavTarget, CurrentNavTarget.Distance2D(myPos), PathPrecision))),
new Action(ret => UpdateRoute())
)
),
new Decorator(ret => CurrentNavTarget.Distance2D(myPos) <= 90f && !MainGridProvider.CanStandAt(MainGridProvider.WorldToGrid(CurrentNavTarget.ToVector2())),
new Sequence(
new Action(ret => SetNodeVisited("Center Not Navigable")),
new Action(ret => UpdateRoute())
)
),
new Decorator(ret => CacheData.NavigationObstacles.Any(o => o.Position.Distance2D(CurrentNavTarget) <= o.Radius * 2),
new Sequence(
new Action(ret => SetNodeVisited("Navigation obstacle detected at node point")),
new Action(ret => UpdateRoute())
)
),
//new Decorator(ret => PlayerMover.MovementSpeed == 0 && myPos.Distance2D(CurrentNavTarget) <= 50f && !Navigator.Raycast(myPos, CurrentNavTarget),
// new Sequence(
// new Action(ret => SetNodeVisited("Stuck moving to node point, marking done (in LoS and nearby!)")),
// new Action(ret => UpdateRoute())
// )
//),
new Decorator(ret => Trinity.SkipAheadAreaCache.Any(p => p.Position.Distance2D(CurrentNavTarget) <= PathPrecision),
new Sequence(
new Action(ret => SetNodeVisited("Found node to be in skip ahead cache, marking done")),
new Action(ret => UpdateRoute())
)
),
CheckIgnoredScenes()
);
}
///
/// Updates the DungeonExplorer Route
///
private void UpdateRoute()
{
CheckResetDungeonExplorer();
BrainBehavior.DungeonExplorer.Update();
PrintNodeCounts("BrainBehavior.DungeonExplorer.Update");
// Throw an exception if this shiz don't work
ValidateCurrentRoute();
}
///
/// Marks the current dungeon Explorer as Visited and dequeues it from the route
///
///
private void SetNodeVisited(string reason = "")
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Dequeueing current node {0} - {1}", BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter, reason);
BrainBehavior.DungeonExplorer.CurrentNode.Visited = true;
BrainBehavior.DungeonExplorer.CurrentRoute.Dequeue();
MarkNearbyNodesVisited();
PrintNodeCounts("SetNodeVisited");
}
public void MarkNearbyNodesVisited()
{
foreach (DungeonNode node in GridSegmentation.Nodes.Where(n => !n.Visited))
{
float distance = node.NavigableCenter.Distance2D(myPos);
if (distance <= PathPrecision)
{
node.Visited = true;
string reason2 = String.Format("Node {0} is within path precision {1:0}/{2:0}", node.NavigableCenter, distance, PathPrecision);
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, "Marking unvisited nearby node as visited - {0}", reason2);
}
}
}
///
/// Makes sure the current route is not null! Bad stuff happens if it's null...
///
private static void ValidateCurrentRoute()
{
if (BrainBehavior.DungeonExplorer.CurrentRoute == null)
{
throw new ApplicationException("DungeonExplorer CurrentRoute is null");
}
}
///
/// Prints a plethora of useful information about the Dungeon Exploration process
///
///
private void PrintNodeCounts(string step = "")
{
if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.ProfileTag))
{
string nodeDistance = String.Empty;
if (GetRouteUnvisitedNodeCount() > 0)
{
try
{
float distance = BrainBehavior.DungeonExplorer.CurrentNode.NavigableCenter.Distance(myPos);
if (distance > 0)
nodeDistance = String.Format("Dist:{0:0}", Math.Round(distance / 10f, 2) * 10f);
}
catch { }
}
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}",
GetRouteVisistedNodeCount(), // 0
GetRouteUnvisitedNodeCount(), // 1
GetGridSegmentationVisistedNodeCount(), // 2
GetGridSegmentationUnvisitedNodeCount(), // 3
GridSegmentation.BoxSize, // 4
GridSegmentation.BoxTolerance, // 5
step, // 6
nodeDistance, // 7
MainGridProvider.CanStandAt(MainGridProvider.WorldToGrid(CurrentNavTarget.ToVector2())), // 8
!Navigator.Raycast(myPos, CurrentNavTarget),
PathPrecision,
MathUtil.GetHeadingToPoint(CurrentNavTarget),
Math.Abs(myPos.Z - CurrentNavTarget.Z)
);
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag, log);
}
}
/*
* Dungeon Explorer Nodes
*/
///
/// Gets the number of unvisited nodes in the DungeonExplorer Route
///
///
private int GetRouteUnvisitedNodeCount()
{
if (GetCurrentRouteNodeCount() > 0)
return BrainBehavior.DungeonExplorer.CurrentRoute.Count(n => !n.Visited);
else
return 0;
}
///
/// Gets the number of visisted nodes in the DungeonExplorer Route
///
///
private int GetRouteVisistedNodeCount()
{
if (GetCurrentRouteNodeCount() > 0)
return BrainBehavior.DungeonExplorer.CurrentRoute.Count(n => n.Visited);
else
return 0;
}
///
/// Gets the number of nodes in the DungeonExplorer Route
///
///
private int GetCurrentRouteNodeCount()
{
if (BrainBehavior.DungeonExplorer.CurrentRoute != null)
return BrainBehavior.DungeonExplorer.CurrentRoute.Count();
else
return 0;
}
/*
* Grid Segmentation Nodes
*/
///
/// Gets the number of Unvisited nodes as reported by the Grid Segmentation provider
///
///
private int GetGridSegmentationUnvisitedNodeCount()
{
if (GetGridSegmentationNodeCount() > 0)
return GridSegmentation.Nodes.Count(n => !n.Visited);
else
return 0;
}
///
/// Gets the number of Visited nodes as reported by the Grid Segmentation provider
///
///
private int GetGridSegmentationVisistedNodeCount()
{
if (GetCurrentRouteNodeCount() > 0)
return GridSegmentation.Nodes.Count(n => n.Visited);
else
return 0;
}
///
/// Gets the total number of nodes with the current BoxSize/Tolerance as reported by the Grid Segmentation Provider
///
///
private int GetGridSegmentationNodeCount()
{
if (GridSegmentation.Nodes != null)
return GridSegmentation.Nodes.Count();
else
return 0;
}
private MoveResult LastMoveResult = MoveResult.Moved;
private DateTime lastGeneratedPath = DateTime.MinValue;
///
/// Moves the bot to the next DungeonExplorer node
///
private void MoveToNextNode()
{
PlayerMover.RecordSkipAheadCachePoint();
NextNode = BrainBehavior.DungeonExplorer.CurrentNode;
Vector3 moveTarget = NextNode.NavigableCenter;
Vector3 lastPlayerMoverTarget = PlayerMover.LastMoveToTarget;
bool isStuck = DateTime.UtcNow.Subtract(PlayerMover.LastRecordedAnyStuck).TotalMilliseconds < 500;
if (isStuck && CacheData.NavigationObstacles.Any(o => MathUtil.IntersectsPath(o.Position, o.Radius, Trinity.Player.Position, lastPlayerMoverTarget)))
{
SetNodeVisited("Nav Obstacle detected!");
UpdateRoute();
return;
}
string nodeName = String.Format("{0} Distance: {1:0} Direction: {2}",
NextNode.NavigableCenter, NextNode.NavigableCenter.Distance(Trinity.Player.Position), MathUtil.GetHeadingToPoint(NextNode.NavigableCenter));
RecordPosition();
LastMoveResult = PlayerMover.NavigateTo(CurrentNavTarget);
//Navigator.MoveTo(CurrentNavTarget);
}
private void RecordPosition()
{
if (DateTime.UtcNow.Subtract(Trinity.lastAddedLocationCache).TotalMilliseconds >= 100)
{
Trinity.lastAddedLocationCache = DateTime.UtcNow;
if (Vector3.Distance(myPos, Trinity.LastRecordedPosition) >= 5f)
{
MarkNearbyNodesVisited();
Trinity.SkipAheadAreaCache.Add(new CacheObstacleObject(myPos, 20f, 0));
Trinity.LastRecordedPosition = myPos;
}
}
}
///
/// Initizializes the profile tag and sets defaults as needed
///
private void Init(bool forced = false)
{
if (BoxSize == 0)
BoxSize = 15;
if (BoxTolerance == 0)
BoxTolerance = 0.55f;
if (PathPrecision == 0)
PathPrecision = BoxSize / 2f;
float minPathPrecision = 15f;
if (PathPrecision < minPathPrecision)
PathPrecision = minPathPrecision;
if (ObjectDistance == 0)
ObjectDistance = 40f;
if (MarkerDistance == 0)
MarkerDistance = 25f;
if (TimeoutValue == 0)
TimeoutValue = 1800;
Trinity.SkipAheadAreaCache.Clear();
PriorityScenesInvestigated.Clear();
MiniMapMarker.KnownMarkers.Clear();
if (PriorityScenes == null)
PriorityScenes = new List();
if (IgnoreScenes == null)
IgnoreScenes = new List();
if (AlternateActors == null)
AlternateActors = new List();
if (!forced)
{
Logger.Log(TrinityLogLevel.Info, LogCategory.ProfileTag,
"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}",
GridSegmentation.BoxSize, GridSegmentation.BoxTolerance, EndType, ExploreTimeoutType, TimeoutValue, PathPrecision, SceneId, ActorId, ObjectDistance, MarkerDistance, ExitNameHash);
}
InitDone = true;
}
private bool isDone = false;
///
/// When true, the next profile tag is used
///
public override bool IsDone
{
get { return !IsActiveQuestStep || isDone; }
}
///
/// Resets this profile tag to defaults
///
public override void ResetCachedDone()
{
isDone = false;
InitDone = false;
}
//Had to use ActiveBounty, caused too much lag constantly searching the bounty list
public bool GetIsBountyDone()
{
// Only valid for Adventure mode
if (ZetaDia.CurrentAct != Act.OpenWorld)
return false;
try
{
var b = ZetaDia.ActInfo.ActiveBounty;
if (b == null)
{
Logger.Log("Active bounty returned null, Assuming done.");
return true;
}
if (!b.Info.IsValid)
{
Logger.Log("Does this even work? Thinks the bounty is not valid.");
}
if (b.Info.QuestSNO > 500000 || b.Info.QuestSNO < 200000)
{
Logger.Log("Got some weird numbers going on with the QuestSNO of the active bounty. Assuming glitched and done.");
return true;
}
//If completed or on next step, we are good.
if (b.Info.State == QuestState.Completed)
{
Logger.Log("Seems completed!");
return true;
}
if (ZetaDia.IsInTown)
{
return true;
}
if (ZetaDia.Me.IsInBossEncounter)
{
return false;
}
}
catch (Exception ex)
{
Logger.LogError("Exception reading ActiveBounty " + ex.Message);
}
return false;
}
}
}
/*
* Never need to call GridSegmentation.Update()
* GridSegmentation.Reset() is automatically called on world change
* DungeonExplorer.Reset() will reset the current route and revisit nodes
* DungeonExplorer.Update() will update the current route to include new scenes
*/