using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using Buddy.Coroutines;
using log4net;
using Loki.Bot;
using Loki.Bot.Pathfinding;
using Loki.Bot.v3;
using Loki.Game;
using Loki.Game.Objects;
using Loki.Utilities;
namespace ExampleRoutine
{
/// <summary> </summary>
public class DualTotemRoutine : IRoutine
{
private static readonly ILog Log = Logger.GetLoggerInstanceForType();
private int _currentLeashRange = -1;
private bool _needsUpdate;
// Do not implement a ctor and do stuff in it.
#region Implementation of IBase
/// <summary>Initializes this routine.</summary>
public void Initialize()
{
Log.DebugFormat("[ExampleRoutine] Initialize");
}
#endregion
#region Implementation of IDisposable
/// <summary> </summary>
public void Dispose()
{
}
#endregion
#region Implementation of IAuthored
/// <summary>The name of the routine.</summary>
public string Name
{
get { return "DualTotemRoutine"; }
}
/// <summary>The description of the routine.</summary>
public string Description
{
get { return "An example routine for Exilebuddy."; }
}
/// <summary>
/// The author of this object.
/// </summary>
public string Author
{
get { return "Bossland GmbH"; }
}
/// <summary>
/// The version of this routone.
/// </summary>
public Version Version
{
get { return new Version(0, 0, 1, 1); }
}
#endregion
#region Implementation of IRunnable
/// <summary> The routine start callback. Do any initialization here. </summary>
public void Start()
{
Log.DebugFormat("[ExampleRoutine] Start");
_needsUpdate = true;
GameEventManager.AreaChanged += GameEventManagerOnAreaChanged;
GameEventManager.PlayerDied += GameEventManagerOnPlayerDied;
}
/// <summary> The routine tick callback. Do any update logic here. </summary>
public void Tick()
{
if (!LokiPoe.IsInGame)
return;
if (_needsUpdate)
{
_needsUpdate = false;
}
}
/// <summary> The routine stop callback. Do any pre-dispose cleanup here. </summary>
public void Stop()
{
Log.DebugFormat("[ExampleRoutine] Stop");
GameEventManager.AreaChanged -= GameEventManagerOnAreaChanged;
}
#endregion
#region Implementation of IConfigurable
/// <summary> The bot's settings control. This will be added to the Exilebuddy Settings tab.</summary>
public UserControl Control
{
get { return null; }
}
/// <summary>The settings object. This will be registered in the current configuration.</summary>
public JsonSettings Settings
{
get { return null; }
}
#endregion
#region Implementation of IRoutine
/// <summary>
/// Sends data to the routine with the associated name.
/// </summary>
/// <param name="name">The name of the configuration.</param>
/// <param name="param">The data passed for the configuration.</param>
public void SetConfiguration(string name, params object[] param)
{
if (name == "leash")
{
_currentLeashRange = (int)param[0];
}
}
/// <summary>
/// Requests data from the routine with the associated name.
/// </summary>
/// <param name="name">The name of the configuration.</param>
/// <returns>Data from the routine.</returns>
public object GetConfiguration(string name)
{
return null;
}
/// <summary>
/// The routine's coroutine logic to execute.
/// </summary>
/// <param name="type">The requested type of logic to execute.</param>
/// <returns></returns>
// ReSharper disable once CSharpWarnings::CS1998
public async Task<bool> Logic(string type)
{
if (type == "combat")
{
var myPos = LokiPoe.Me.Position;
// TODO: _currentLeashRange of -1 means we need to use a cached location system to prevent back and forth issues of mobs despawning.
var monsters =
LokiPoe.ObjectManager.GetObjectsByType<Monster>()
.Where(
m =>
!AreaStateCache.Current.IsBlacklisted(m.Id) &&
!m.CannotDie &&
m.Distance < (_currentLeashRange != -1 ? _currentLeashRange : 50) &&
m.IsActive)
.OrderBy(m => m.Distance).ToList();
// No monsters, we can execute non-critical combat logic, like buffs, auras, etc...
// For this example, just going to continue executing bot logic.
if (monsters.Count == 0)
{
return false;
}
// This is pretty important. Otherrwise, components can go invalid and exceptions are thrown.
var bestTarget = monsters.First();
var cachedPosition = bestTarget.Position;
var cachedId = bestTarget.Id;
var cachedName = bestTarget.Name;
var canSee = ExilePather.CanObjectSee(LokiPoe.Me, bestTarget);
var pathDistance = ExilePather.PathDistance(myPos, cachedPosition);
var blockedByDoor = Utility.ClosedDoorBetween(LokiPoe.Me, bestTarget);
if (pathDistance.CompareTo(float.MaxValue) == 0)
{
Log.ErrorFormat(
"[Logic] Could not determine the path distance to the best target. Now blacklisting it.");
AreaStateCache.Current.Blacklist(cachedId, TimeSpan.FromMinutes(1), "Unable to pathfind to.");
return true;
}
// Prevent combat loops from happening.
if (pathDistance > 30)
return false;
if (!canSee || blockedByDoor)
{
Log.InfoFormat(
"[Logic] Now moving towards the monster {0} because [canSee: {1}][pathDistance: {2}][blockedByDoor: {3}]",
cachedName, canSee, pathDistance, blockedByDoor);
if (!Functions.MoveTowards(cachedPosition))
{
}
return true;
}
var totemSkill = LokiPoe.InGameState.SkillBarPanel.Slot2;
var totmes = totemSkill.DeployedObjects.ToList();
var oldestTotem = totmes.Count > 0 ? totmes[0] : null;
var newestTotem = totmes.Count > 1 ? totmes[1] : null;
var frontPosition = ExilePather.WalkablePositionFor(Utility.CalculatePointAtDistanceBeforeEnd(myPos, cachedPosition, 10), 5);
var backPosition = ExilePather.WalkablePositionFor(Utility.CalculatePointAtDistanceAfterEnd(myPos, cachedPosition, 10), 5);
if (oldestTotem == null || newestTotem == null || oldestTotem.Position.Distance(frontPosition) > 15 ||
newestTotem.Position.Distance(backPosition) > 15)
{
await Coroutines.FinishCurrentAction();
await Coroutine.Sleep(Utility.LatencySafeValue(100));
while (!LokiPoe.Me.HasCurrentAction)
{
LokiPoe.InGameState.SkillBarPanel.UseAt(totemSkill.Slot, true, frontPosition);
await Coroutine.Sleep(Utility.LatencySafeValue(25));
}
while (LokiPoe.Me.HasCurrentAction)
{
await Coroutine.Sleep(Utility.LatencySafeValue(25));
}
await Coroutine.Sleep(Utility.LatencySafeValue(100));
while (!LokiPoe.Me.HasCurrentAction)
{
LokiPoe.InGameState.SkillBarPanel.UseAt(totemSkill.Slot, true, backPosition);
await Coroutine.Sleep(Utility.LatencySafeValue(25));
}
while (LokiPoe.Me.HasCurrentAction)
{
await Coroutine.Sleep(Utility.LatencySafeValue(25));
}
await Coroutine.Sleep(Utility.LatencySafeValue(100));
}
return true;
}
return false;
}
#endregion
#region Override of Object
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Name + ": " + Description;
}
#endregion
private void GameEventManagerOnAreaChanged(object sender, AreaChangedEventArgs areaChangedEventArgs)
{
}
private void GameEventManagerOnPlayerDied(object sender, PlayerDiedEventArgs playerDiedEventArgs)
{
}
}
}