using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Markup;
using log4net;
using Loki.Bot.Logic.Bots.ExilebuddyBot;
using Loki.Bot.v2;
using Loki.Bot.v2.Settings;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Objects;
using Loki.Utilities;
// ReSharper disable once CheckNamespace
namespace ExampleRoutineNamespace
{
/// <summary> </summary>
public class ExampleRoutine : IRoutine
{
private static readonly ILog Log = Logger.GetLoggerInstanceForType();
private bool _needsToAura = true;
// Do not implement a ctor.
#region Implementation of IRoutine
/// <summary>The name of the routine.</summary>
public string Name
{
get { return "ExampleRoutine"; }
}
/// <summary>The description of the routine.</summary>
public string Description
{
get { return "An example routine for Exilebuddy."; }
}
/// <summary>Initializes this combat routine.</summary>
public void Initialize()
{
Log.DebugFormat("[ExampleRoutine] Initialize");
// Register the settings manager for this routine with the configuration manager.
Configuration.Instance.AddSettings(ExampleRoutineSettings.Instance);
}
/// <summary> The routine start callback. Do any initialization here. </summary>
public void OnStart()
{
Log.DebugFormat("[ExampleRoutine] OnStart");
GameEventManager.AreaChanged += GameEventManagerOnAreaChanged;
GameEventManager.PlayerDied += GameEventManagerOnPlayerDied;
}
/// <summary> The routine tick callback. Do any update logic here. </summary>
public void OnTick()
{
}
/// <summary> The routine stop callback. Do any pre-dispose cleanup here. </summary>
public void OnStop()
{
Log.DebugFormat("[ExampleRoutine] OnStop");
GameEventManager.PlayerDied -= GameEventManagerOnPlayerDied;
GameEventManager.AreaChanged -= GameEventManagerOnAreaChanged;
}
/// <summary> The bot's settings control. This will be added to the Exilebuddy Settings tab.</summary>
public UserControl SettingsControl
{
get
{
using (var fs = new FileStream(@"Routines\ExampleRoutine\SettingsGui.xaml", FileMode.Open))
{
var root = (UserControl) XamlReader.Load(fs);
// TODO: Your settings binding here.
// TODO: Your settings event handlers here.
return root;
}
}
}
private readonly List<string> _auras = new List<string> {"Haste", "Clarity"};
private readonly List<string> _buffs = new List<string> { "Molten Shell" };
/// <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)
{
// Handle aura logic. This is just an example from ExilebuddyRoutine; you do not have to implement aura logic this way.
if (type == "aura")
{
if (!_needsToAura)
return false;
foreach (var aura in _auras)
{
// This assumes you only have one copy of the skill avaliable.
var spell = LokiPoe.SpellManager.GetSpellByName(aura);
if (spell == null)
continue;
// We need to cache this since objects might invalidate during coroutine continuation.
var spellId = spell.Id;
var spellName = spell.Name;
// If we already have the aura, don't cast it again.
if (LokiPoe.Me.HasAura(spellName))
{
continue;
}
if (!spell.CanCast(true, true))
return true;
var res = await Coroutines.SmartCastAuraCoroutine(spellId);
if (res != Coroutines.SmartCastCoroutineError.None)
Log.DebugFormat("[Logic] SmartCastCoroutine returned {0} when casting {1}.", res, spellName);
if (res == Coroutines.SmartCastCoroutineError.None)
return true;
}
_needsToAura = false;
return false;
}
// Handle buff logic. This is just an example from ExilebuddyRoutine; you do not have to implement buff logic this way.
if (type == "buff")
{
var ret = false;
foreach (var buff in _buffs)
{
// This assumes you only have one copy of the skill.
var spell = LokiPoe.SpellManager.GetSpellByName(buff);
if (spell == null)
continue;
// We need to cache this since objects might invalidate during coroutine continuation.
var spellId = spell.Id;
var spellName = spell.Name;
// If we are under the effects of this buff, skip casting it.
if (LokiPoe.Me.HasAuraFrom(spellName))
continue;
if (!spell.CanCast(false, true))
continue;
var res = await Coroutines.SmartCastBuffCoroutine(spellId);
if (res != Coroutines.SmartCastCoroutineError.None)
Log.DebugFormat("[Logic] SmartCastCoroutine returned {0} when casting {1}.", res, spellName);
if (res == Coroutines.SmartCastCoroutineError.None)
ret = true;
}
return ret;
}
// Handle combat logic.
if (type == "combat")
{
var target = BestTarget;
if (target == null)
return false;
// If the target is too far, try to move towards it some.
if (!target.IsInLineOfSight || target.Distance > 40)
{
var res = await Coroutines.ClickToMoveTowards(target.Position);
if (res != Coroutines.ClickToMoveTowardsError.None)
{
AreaStateCache.Current.Blacklist(target.Id, TimeSpan.FromMilliseconds(1000), "Could not ClickToMoveTowards the target.");
}
return true;
}
var targetId = target.Id;
var targetName = target.Name;
var targetDistance = target.Distance;
var targetInLos = target.IsInLineOfSight;
var targetRarity = target.Rarity;
var mobsNearTarget = Utility.NumberOfMobsNear(target, 24);
var isCursable = target.IsCursable;
var isCursedWithTemporalChains = target.IsCursedWithTemporalChains;
if (isCursable && !isCursedWithTemporalChains)
{
if (await Cast("Temporal Chains", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
if (targetDistance > 24 && targetInLos)
{
if (await Cast("Leap Slam", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
if (mobsNearTarget > 2 && targetRarity >= Rarity.Rare)
{
if (await Cast("Ground Slam", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
if (targetRarity >= Rarity.Rare)
{
if (await Cast("Heavy Strike", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
if (mobsNearTarget > 1)
{
if (await Cast("Ground Slam", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
if (await Cast("Heavy Strike", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
if (await Cast("Default Attack", targetId) == Coroutines.SmartCastCoroutineError.None)
return true;
}
return false;
}
#endregion
private async Task<Coroutines.SmartCastCoroutineError> Cast(string name, int targetId)
{
var spell = LokiPoe.SpellManager.GetSpellByName(name);
if (spell != null)
{
return await Coroutines.SmartCastCoroutine(spell.Id, targetId);
}
return Coroutines.SmartCastCoroutineError.NoSpell;
}
#region Implementation of IDisposable
/// <summary> </summary>
public void Dispose()
{
// Unregister the settings manager for this routine with the configuration manager.
Configuration.Instance.RemoveSettings(ExampleRoutineSettings.Instance);
}
#endregion
#region Override of Object
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Name + ": " + Description;
}
#endregion
/// <summary>Returns a consistent best target from the target list.</summary>
public Monster BestTarget
{
get { return Targeting.Combat.Targets.FirstOrDefault() as Monster; }
}
private void GameEventManagerOnPlayerDied(object sender, PlayerDiedEventArgs playerDiedEventArgs)
{
_needsToAura = true;
}
private void GameEventManagerOnAreaChanged(object sender, AreaChangedEventArgs areaChangedEventArgs)
{
_needsToAura = true;
}
}
}