Skip to content

Instantly share code, notes, and snippets.

@Cholik
Created August 29, 2015 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cholik/85546f125b014e6c0a92 to your computer and use it in GitHub Desktop.
Save Cholik/85546f125b014e6c0a92 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using Buddy.Coroutines;
using log4net;
using Loki.Bot;
using Loki.Bot.Logic.Bots.OldGrindBot;
using Loki.Bot.Pathfinding;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Objects;
using Loki.Common;
using Loki.Game.Objects.Items;
using Utility = Loki.Bot.Logic.Bots.OldGrindBot.Utility;
namespace Typhonis
{
/// <summary> </summary>
public class Typhonis : IRoutine
{
private static readonly ILog Log = Logger.GetLoggerInstanceForType();
public static IEnumerable<Item> RarityFlasks
{
get
{
var inv = LokiPoe.InGameState.QuickFlaskPanel.Flasks;
return from item in inv
let flask = item as Flask
where flask != null && flask.FullName == "Divination Distillate" && flask.CanUse
select item;
}
}
void forceFirstWeapon()
{
Log.Alert("HAS ENTERED FORCE FIRST WEAPON");
if (Loki.Game.LokiPoe.InGameState.InventoryPanel.LeftHand.Name == "Shadow Axe")
{
//Loki.Game.GameData.ActionKeys.weapon_swap();
//Loki.Game.LokiPoe.Input.Binding.weapon_swap
Loki.Game.LokiPoe.Input.PressKey(Loki.Game.LokiPoe.Input.Binding.weapon_swap);
}
}
void forceSecondWeapon()
{
if (Loki.Game.LokiPoe.InGameState.InventoryPanel.LeftHand.Name != "Shadow Axe")
{
Loki.Game.LokiPoe.Input.PressKey(Loki.Game.LokiPoe.Input.Binding.weapon_swap);
}
}
// Auto-set, you do not have to change these.
private bool FlaskHelper(Stopwatch sw, int flaskCdMs, IEnumerable<Item> flasks)
{
var useFlask = false;
if (!sw.IsRunning)
{
sw.Start();
useFlask = true;
}
else if (sw.ElapsedMilliseconds > Loki.Bot.Logic.Bots.OldGrindBot.Utility.LatencySafeValue(flaskCdMs))
{
sw.Restart();
useFlask = true;
}
if (useFlask)
{
var flask = flasks.FirstOrDefault();
if (flask != null)
{
var err = LokiPoe.InGameState.QuickFlaskPanel.UseFlask(flask);
if (err != LokiPoe.InGameState.UseFlaskError.None)
{
Log.ErrorFormat("[FlaskHelper] QuickFlaskPanel.UseFlask returned {0}.", err);
}
return true;
}
}
return false;
}
int distanceTest = 12;
private int _enduringCrySlot = -1;
private int _moltenShellSlot = -1;
private int _leapSlamSlot = -1;
private int _golemSlot = -1;
private int _aaSlot = -1;
private int _heraldOfAshSlot = -1;
private int _rfSlot = -1;
private int _cycloneSlot = -1;
private readonly List<int> _curseSlots = new List<int>();
private int _auraSlot = -1;
private int _currentLeashRange = -1;
private readonly Stopwatch _moltenShellStopwatch = Stopwatch.StartNew();
private readonly List<int> _ignoreAnimatedItems = new List<int>();
private readonly Stopwatch _vaalStopwatch = Stopwatch.StartNew();
private readonly Stopwatch _golemStopwatch = Stopwatch.StartNew();
private readonly Stopwatch _cycloneStopwatch = Stopwatch.StartNew();
private readonly Stopwatch _rarityFlaskCd = new Stopwatch();
private bool _needsUpdate;
private readonly Targeting _combatTargeting = new Targeting();
private bool BlacklistedSkill(int id)
{
return false;
}
private Targeting CombatTargeting
{
get
{
return _combatTargeting;
}
}
// Do not implement a ctor and do stuff in it.
#region Targeting
private void CombatTargetingOnWeightCalculation(NetworkObject entity, ref float weight)
{
weight -= entity.Distance / 2;
var m = entity as Monster;
if (m == null)
return;
// If the monster is the source of Allies Cannot Die, we really want to kill it fast.
if (m.HasAura("monster_aura_cannot_die"))
weight += 40;
if (m.IsTargetingMe)
{
weight += 20;
}
if (m.Rarity == Rarity.Magic)
{
weight += 5;
}
else if (m.Rarity == Rarity.Rare)
{
weight += 10;
}
else if (m.Rarity == Rarity.Unique)
{
weight += 15;
}
// Minions that get in the way.
switch (m.Name)
{
case "Summoned Skeleton":
weight -= 15;
break;
case "Raised Zombie":
weight -= 15;
break;
}
if (m.Rarity == Rarity.Normal && m.Type.Contains("/Totems/"))
{
weight -= 15;
}
// Necros
if (m.ExplicitAffixes.Any(a => a.InternalName.Contains("RaisesUndead")) ||
m.ImplicitAffixes.Any(a => a.InternalName.Contains("RaisesUndead")))
{
weight += 30;
}
// Ignore these mostly, as they just respawn.
if (m.Type.Contains("TaniwhaTail"))
{
weight -= 30;
}
}
private readonly string[] _aurasToIgnore = new[]
{
"shrine_godmode", // Ignore any mob near Divine Shrine
"bloodlines_invulnerable", // Ignore Phylacteral Link
"god_mode", // Ignore Animated Guardian
"bloodlines_necrovigil",
};
private bool CombatTargetingOnInclusionCalcuation(NetworkObject entity)
{
try
{
var m = entity as Monster;
if (m == null)
return false;
if (AreaStateCache.Current.IsBlacklisted(m))
return false;
// Do not consider inactive/dead mobs.
if (!m.IsActive)
return false;
// Ignore any mob that cannot die.
if (m.CannotDie)
return false;
// Ignore mobs that are too far to care about.
if (m.Distance > (_currentLeashRange != -1 ? TyphonisSettings.Instance.CombatRange : 300))
return false;
// Ignore mobs with special aura/buffs
if (m.HasAura(_aurasToIgnore))
return false;
// Ignore Voidspawn of Abaxoth: thanks ExVault!
if (m.ExplicitAffixes.Any(a => a.DisplayName == "Voidspawn of Abaxoth"))
return false;
// Ignore these mobs when trying to transition in the dom fight.
// Flag1 has been seen at 5 or 6 at times, so need to work out something more reliable.
if (m.Name == "Miscreation")
{
var dom = LokiPoe.ObjectManager.GetObjectByName<Monster>("Dominus, High Templar");
if (dom != null && !dom.IsDead && (dom.Components.TransitionableComponent.Flag1 == 6 || dom.Components.TransitionableComponent.Flag1 == 5))
{
AreaStateCache.Current.Blacklist(m.Id, TimeSpan.FromSeconds(7), "Miscreation");
return false;
}
}
// Ignore Piety's portals.
if (m.Name == "Chilling Portal" || m.Name == "Burning Portal")
{
AreaStateCache.Current.Blacklist(m.Id, TimeSpan.FromHours(1), "Piety portal");
return false;
}
}
catch (Exception ex)
{
Log.Error("[CombatOnInclusionCalcuation]", ex);
return false;
}
return true;
}
#endregion
#region Implementation of IBase
/// <summary>Initializes this routine.</summary>
public void Initialize()
{
Log.DebugFormat("[Typhonis] Initialize");
_combatTargeting.InclusionCalcuation += CombatTargetingOnInclusionCalcuation;
_combatTargeting.WeightCalculation += CombatTargetingOnWeightCalculation;
}
#endregion
#region Implementation of IDisposable
/// <summary> </summary>
/// <summary> </summary>
public void Deinitialize()
{
}
#endregion
#region Implementation of IAuthored
/// <summary>The name of the routine.</summary>
public string Name
{
get { return "Typhonis"; }
}
/// <summary>The description of the routine.</summary>
public string Description
{
get { return "A Cyclone Routine for Exilebuddy."; }
}
/// <summary>
/// The author of this object.
/// </summary>
public string Author
{
get { return "Tozededao"; }
}
/// <summary>
/// The version of this routone.
/// </summary>
public string Version
{
get { return "0, 0, 0, 1"; }
}
#endregion
#region Implementation of IRunnable
/// <summary> The routine start callback. Do any initialization here. </summary>
public void Start()
{
Log.DebugFormat("[Typhonis] Start");
_needsUpdate = true;
}
private int EnduranceCharges
{
get
{
Aura aura = LokiPoe.ObjectManager.Me.Auras.FirstOrDefault(a => a.InternalName == "endurance_charge");
if (aura != null)
{
return aura.Charges;
}
return 0;
}
}
private TimeSpan EnduranceChargesLeft
{
get
{
Aura aura = LokiPoe.ObjectManager.Me.Auras.FirstOrDefault(a => a.InternalName == "endurance_charge");
if (aura != null)
{
return aura.TimeLeft;
}
return TimeSpan.Zero;
}
}
private bool IsCastableHelper(Skill skill)
{
return skill != null && skill.IsCastable && !skill.IsTotem && !skill.IsTrap && !skill.IsMine;
}
private bool IsAuraName(string name)
{
// This makes sure auras on items don't get used, since they don't have skill gems, and won't have an Aura tag.
if (!TyphonisSettings.Instance.EnableAurasFromItems)
{
return false;
}
var auraNames = new string[]
{
"Anger", "Clarity", "Determination", "Discipline", "Grace", "Haste", "Hatred", "Herald of Ash", "Arctic Armour", "Purity of Elements",
"Purity of Fire", "Purity of Ice", "Purity of Lightning", "Vitality", "Wrath"
};
return auraNames.Contains(name);
}
/// <summary> The routine tick callback. Do any update logic here. </summary>
public void Tick()
{
if (!LokiPoe.IsInGame)
return;
if (_needsUpdate)
{
_enduringCrySlot = -1;
_moltenShellSlot = -1;
_golemSlot = -1;
_auraSlot = -1;
_aaSlot = -1;
_heraldOfAshSlot = -1;
_rfSlot = -1;
_leapSlamSlot = -1;
_curseSlots.Clear();
// Register curses.
foreach (var skill in LokiPoe.InGameState.SkillBarPanel.Skills)
{
var tags = skill.SkillTags;
var name = skill.Name;
if (tags.Contains("curse"))
{
var slot = skill.Slot;
if (slot != -1 && skill.IsCastable)
{
_curseSlots.Add(slot);
}
}
if (_auraSlot == -1 && ((tags.Contains("aura") && !tags.Contains("vaal")) || IsAuraName(name)))
{
_auraSlot = skill.Slot;
}
}
var rf = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Righteous Fire");
if (IsCastableHelper(rf))
{
_rfSlot = rf.Slot;
}
var cyclone = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Cyclone");
if (IsCastableHelper(cyclone))
{
_cycloneSlot = cyclone.Slot;
}
var aa = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Arctic Armour");
if (IsCastableHelper(aa))
{
_aaSlot = aa.Slot;
}
var hoa = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Herald of Ash");
if (IsCastableHelper(aa))
{
_heraldOfAshSlot = hoa.Slot;
}
var mc = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Molten Shell");
if (IsCastableHelper(mc))
{
_moltenShellSlot = mc.Slot;
}
var golem = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Summon Flame Golem");
if (IsCastableHelper(golem))
{
_golemSlot = golem.Slot;
}
golem = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Summon Ice Golem");
if (IsCastableHelper(golem))
{
_golemSlot = golem.Slot;
}
golem = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Summon Chaos Golem");
if (IsCastableHelper(golem))
{
_golemSlot = golem.Slot;
}
var ec = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Enduring Cry");
if (IsCastableHelper(ec))
{
_enduringCrySlot = ec.Slot;
}
var ls = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Leap Slam");
if (IsCastableHelper(ls))
{
_leapSlamSlot = ls.Slot;
}
_needsUpdate = false;
}
}
/// <summary> The routine stop callback. Do any pre-dispose cleanup here. </summary>
public void Stop()
{
Log.DebugFormat("[Typhonis] Stop");
}
#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
{
using (var fs = new FileStream(@"Routines\Typhonis\SettingsGui.xaml", FileMode.Open))
{
var root = (UserControl)XamlReader.Load(fs);
// Your settings binding here.
if (
!Wpf.SetupCheckBoxBinding(root, "EnableAurasFromItemsCheckBox", "EnableAurasFromItems",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'EnableAurasFromItemsCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (
!Wpf.SetupCheckBoxBinding(root, "AlwaysAttackInPlaceCheckBox", "AlwaysAttackInPlace",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'AlwaysAttackInPlaceCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (
!Wpf.SetupCheckBoxBinding(root, "FarmDominusCheckBox", "FarmDominus",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'FarmDominusCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (
!Wpf.SetupCheckBoxBinding(root, "UseRandomTargetingCheckBox", "UseRandomTargeting",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'UseRandomTargetingCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (
!Wpf.SetupCheckBoxBinding(root, "DebugAurasCheckBox", "DebugAuras",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'DebugAurasCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (
!Wpf.SetupCheckBoxBinding(root, "AutoCastVaalSkillsCheckBox", "AutoCastVaalSkills",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat(
"[SettingsControl] SetupCheckBoxBinding failed for 'AutoCastVaalSkillsCheckBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (!Wpf.SetupTextBoxBinding(root, "CombatRangeTextBox", "CombatRange",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'CombatRangeTextBox'.");
throw new Exception("The SettingsControl could not be created.");
}
if (!Wpf.SetupTextBoxBinding(root, "CycloneRangeTextBox", "cycloneRange",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'cycloneRange'.");
throw new Exception("The SettingsControl could not be created.");
}
if (!Wpf.SetupTextBoxBinding(root, "CycloneTargetRangeTextBox", "cycloneTargetRange",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'cycloneTargetRange'.");
throw new Exception("The SettingsControl could not be created.");
}
if (!Wpf.SetupTextBoxBinding(root, "CycloneTargetMaxRangeTextBox", "cycloneTargetMaxRange",
BindingMode.TwoWay, TyphonisSettings.Instance))
{
Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'cycloneTargetMaxRange'.");
throw new Exception("The SettingsControl could not be created.");
}
// Your settings event handlers here.
return root;
}
}
}
/// <summary>The settings object. This will be registered in the current configuration.</summary>
public JsonSettings Settings
{
get { return TyphonisSettings.Instance; }
}
#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 object Execute(string name, params object[] param)
{
if (name == "SetLeash")
{
_currentLeashRange = (int)param[0];
}
return null;
}
/// <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;
}
private async Task<bool> CombatLogicEnd()
{
await EnableAlwaysHiglight();
return false;
}
private async Task<bool> HandleShrines()
{
// TODO: Shrines need speical CR logic, because it's now the CRs responsibility for handling all combaat situations,
// and shrines are now considered a combat situation due their nature.
// Check for any active shrines.
var shrines =
LokiPoe.ObjectManager.Objects.OfType<Shrine>()
.Where(s => !s.IsDeactivated && s.Distance < 50)
.OrderBy(s => s.Distance)
.ToList();
if (!shrines.Any())
return false;
// For now, just take the first shrine found.
var shrine = shrines[0];
// Handle Skeletal Shrine in a special way, or handle priority between multiple shrines at the same time.
var skellyOverride = shrine.ShrineId == "Skeletons";
// Try and only move to touch it when we have a somewhat navigable path.
if ((Utility.NumberOfMobsBetween(LokiPoe.Me, shrine) < 5 &&
Utility.NumberOfMobsNear(LokiPoe.Me, 20) < 3) || skellyOverride)
{
Log.DebugFormat("[Logic] Now moving towards the Shrine {0}.", shrine.Id);
var myPos = LokiPoe.Me.Position;
var pos = ExilePather.WalkablePositionFor(shrine, 10);
// We need to filter out based on pathfinding, since otherwise, a large gap will lockup the bot.
var pathDistance = ExilePather.PathDistance(myPos, pos);
if (pathDistance > 50)
{
return false;
}
var canSee = ExilePather.CanObjectSee(LokiPoe.Me, pos);
var inDistance = myPos.Distance(pos) < 20;
if (canSee && inDistance)
{
Log.DebugFormat("[Logic] Now attempting to interact with the Shrine {0}.", shrine.Id);
await Coroutines.FinishCurrentAction();
await Coroutines.InteractWith(shrine);
await Coroutine.Sleep(500);
}
else
{
Log.DebugFormat("[HandleShrines] Moving towards {0}. [canSee: {1} | inDistance: {2}]", pos, canSee,
inDistance);
if (!PlayerMover.MoveTowards(pos))
{
Log.ErrorFormat("[HandleShrines] MoveTowards failed for {0}.", pos);
}
}
return true;
}
return false;
}
private bool _needsToDisableAlwaysHighlight;
// This logic is now CR specific, because Strongbox gui labels interfere with targeting,
// but not general movement using Move only.
private async Task DisableAlwaysHiglight()
{
if (_needsToDisableAlwaysHighlight && LokiPoe.InGameState.IsAlwaysHighlightEnabled)
{
Log.InfoFormat("[DisableAlwaysHiglight] Now disabling Always Highlight to avoid skill use issues.");
LokiPoe.Input.PressKey(LokiPoe.Input.Binding.highlight_toggle);
await Coroutine.Sleep(16);
}
}
// This logic is now CR specific, because Strongbox gui labels interfere with targeting,
// but not general movement using Move only.
private async Task EnableAlwaysHiglight()
{
if (!LokiPoe.InGameState.IsAlwaysHighlightEnabled)
{
Log.InfoFormat("[EnableAlwaysHiglight] Now enabling Always Highlight.");
LokiPoe.Input.PressKey(LokiPoe.Input.Binding.highlight_toggle);
await Coroutine.Sleep(16);
}
}
/// <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, params object[] param)
{
if (type == "core_area_changed_event")
{
var oldSeed = (uint)param[0];
var newSeed = (uint)param[1];
var oldArea = (DatWorldAreaWrapper)param[2];
var newArea = (DatWorldAreaWrapper)param[3];
_ignoreAnimatedItems.Clear();
return true;
}
if (type == "core_player_died_event")
{
var totalDeathsForInstance = (int)param[0];
return true;
}
if (type == "core_player_leveled_event")
{
Log.InfoFormat("[Logic] We are now level {0}!", (int)param[0]);
return true;
}
if (type == "combat")
{
// Update targeting.
CombatTargeting.Update();
// We now signal always highlight needs to be disabled, but won't actually do it until we cast something.
if (
LokiPoe.ObjectManager.GetObjectsByType<Chest>()
.Any(c => c.Distance < 70 && !c.IsOpened && c.IsStrongBox))
{
_needsToDisableAlwaysHighlight = true;
}
else
{
_needsToDisableAlwaysHighlight = false;
}
var myPos = LokiPoe.Me.Position;
#region auras and buffs to cast precombat
// Handle aura logic.
if (_auraSlot != -1)
{
var aura = LokiPoe.InGameState.SkillBarPanel.Slot(_auraSlot);
if (!LokiPoe.Me.HasAura(aura.Name) && aura.CanUse(TyphonisSettings.Instance.DebugAuras))
{
await Coroutines.FinishCurrentAction();
await Coroutine.Sleep(Utility.LatencySafeValue(100));
var err1 = LokiPoe.InGameState.SkillBarPanel.Use(_auraSlot, false);
if (err1 == LokiPoe.InGameState.UseError.None)
{
await Coroutine.Sleep(Utility.LatencySafeValue(250));
await Coroutines.FinishCurrentAction(false);
await Coroutine.Sleep(Utility.LatencySafeValue(100));
return true;
}
Log.ErrorFormat("[Logic] Use returned {0} for {1}.", err1, aura.Name);
}
foreach (var skill in LokiPoe.InGameState.SkillBarPanel.Skills)
{
if ((skill.SkillTags.Contains("aura") && !skill.SkillTags.Contains("vaal")) || IsAuraName(skill.Name))
{
if (!LokiPoe.Me.HasAura(skill.Name) && skill.CanUse(TyphonisSettings.Instance.DebugAuras, true))
{
var doCast = true;
while (skill.Slot == -1)
{
Log.InfoFormat("[Logic] Now assigning {0} to the skillbar.", skill.Name);
var sserr = LokiPoe.InGameState.SkillBarPanel.SetSlot(_auraSlot, skill);
if (sserr != LokiPoe.InGameState.SetSlotError.None)
{
Log.ErrorFormat("[Logic] SetSlot returned {0}.", sserr);
doCast = false;
break;
}
await Coroutine.Sleep(Utility.LatencySafeValue(100));
}
if (!doCast)
{
continue;
}
await Coroutines.FinishCurrentAction();
await Coroutine.Sleep(Utility.LatencySafeValue(100));
var err1 = LokiPoe.InGameState.SkillBarPanel.Use(skill.Slot, false);
if (err1 == LokiPoe.InGameState.UseError.None)
{
await Coroutine.Sleep(Utility.LatencySafeValue(250));
await Coroutines.FinishCurrentAction(false);
await Coroutine.Sleep(Utility.LatencySafeValue(100));
return true;
}
Log.ErrorFormat("[Logic] Use returned {0} for {1}.", err1, skill.Name);
}
}
}
}
if (_golemSlot != -1 && _golemStopwatch.ElapsedMilliseconds > 10000)
{
// See if we can use the skill.
var skill = LokiPoe.InGameState.SkillBarPanel.Slot(_golemSlot);
if (skill.CanUse() && skill.NumberDeployed < 1)
{
var err1 = LokiPoe.InGameState.SkillBarPanel.Use(_golemSlot, true);
_golemStopwatch.Restart();
if (err1 == LokiPoe.InGameState.UseError.None)
{
//await Coroutine.Sleep(Utility.LatencySafeValue(500));
await Coroutines.FinishCurrentAction(false);
await Coroutine.Sleep(Utility.LatencySafeValue(100));
return true;
}
Log.ErrorFormat("[Logic] Use returned {0} for {1}.", err1, skill.Name);
}
}
#endregion
// TODO: _currentLeashRange of -1 means we need to use a cached location system to prevent back and forth issues of mobs despawning.
// This is pretty important. Otherwise, components can go invalid and exceptions are thrown.
var bestTarget = CombatTargeting.Targets<Monster>().FirstOrDefault();
// No monsters, we can execute non-critical combat logic, like buffs, auras, etc...
// For this example, just going to continue executing bot logic.
if (bestTarget == null)
{
if (await HandleShrines())
{
return true;
}
return await CombatLogicEnd();
}
var cachedPosition = bestTarget.Position;
var targetPosition = bestTarget.ModelCenterWorld;
var cachedId = bestTarget.Id;
var cachedName = bestTarget.Name;
var cachedRarity = bestTarget.Rarity;
var cachedDistance = bestTarget.Distance;
var cachedIsCursable = bestTarget.IsCursable;
var cachedCurseCount = bestTarget.CurseCount;
var cachedHasCurseFrom = new Dictionary<string, bool>();
var cachedNumberOfMobsNear = Utility.NumberOfMobsNear(bestTarget, 20);
var cachedProxShield = bestTarget.HasProximityShield;
var cachedMobsNearForAoe = Utility.NumberOfMobsNear(LokiPoe.Me,
TyphonisSettings.Instance.MaxMeleeRange);
var cachedMobsNearForCurse = Utility.NumberOfMobsNear(bestTarget, 20);
foreach (var curseSlot in _curseSlots)
{
var skill = LokiPoe.InGameState.SkillBarPanel.Slot(curseSlot);
cachedHasCurseFrom.Add(skill.Name, bestTarget.HasCurseFrom(skill.Name));
}
var canSee = ExilePather.CanObjectSee(LokiPoe.Me, bestTarget);
var pathDistance = ExilePather.PathDistance(myPos, cachedPosition);
var blockedByDoor = Utility.ClosedDoorBetween(LokiPoe.Me, bestTarget);
if (await HandleShrines())
{
return true;
}
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 by preventing combat outside CombatRange.
if (pathDistance > TyphonisSettings.Instance.CombatRange)
{
await EnableAlwaysHiglight();
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 (!PlayerMover.MoveTowards(cachedPosition))
{
Log.ErrorFormat("[Logic] MoveTowards failed for {0}.", cachedPosition);
}
return true;
}
int cycloneRange = TyphonisSettings.Instance.cycloneRange;
int cycloneTargetRange = TyphonisSettings.Instance.cycloneTargetRange;
int cycloneMaxTargetRange = TyphonisSettings.Instance.cycloneTargetMaxRange;
bool testeThis = false;
// Cyclone to engage the fight
if (_cycloneSlot != -1)
{
if (Utility.NumberOfMobsNear(LokiPoe.Me, cycloneTargetRange) > 0 && !LokiPoe.Me.IsDead)
{
if (cachedDistance < cycloneTargetRange)
{
if (testeThis)
{
//System.Windows.Forms.Keys cycloneKey = System.Windows.Forms.Keys.Q;
//if (cachedDistance < cycloneTargetRange)
//{
// Loki.Game.LokiPoe.ProcessHookManager.SetKeyState(cycloneKey, 1);
// // Loki.Game.LokiPoe.ProcessHookManager.SetCursorPos(cachedPosition.X, cachedPosition.Y, 1);
// Log.ErrorFormat(" Cyclone");
//}
//Loki.Game.LokiPoe.ProcessHookManager.SetKeyState(cycloneKey, 0);
//Log.ErrorFormat("Stop Cyclone");
if (TyphonisSettings.Instance.FarmDominus)
{
forceFirstWeapon();
}
Vector2i randomPosition2 = Loki.Bot.Pathfinding.ExilePather.FindWalkableLocationsCloseTo(bestTarget, 4).First();
LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, randomPosition2);
}
else
{
if (TyphonisSettings.Instance.UseRandomTargeting)
{
Vector2i randomPosition;
double angle = 2.0 * Math.PI * Loki.Game.LokiPoe.Random.NextDouble();
randomPosition.X = (int)(myPos.X + cycloneMaxTargetRange * Math.Cos(angle));
randomPosition.Y = (int)(myPos.Y + cycloneMaxTargetRange * Math.Sin(angle));
LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, randomPosition);
Log.ErrorFormat("Attack at random pos around me");
}
else
{
LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, myPos.GetPointAtDistanceAfterEnd(cachedPosition, cycloneMaxTargetRange));
LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, myPos.GetPointAtDistanceBeforeThis(cachedPosition, cycloneMaxTargetRange));
Log.ErrorFormat("Attack After End", cachedDistance);
}
}
}
else
{
Vector2i randomPosition2 = Loki.Bot.Pathfinding.ExilePather.FindWalkableLocationsCloseTo(bestTarget, 4).First();
LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, randomPosition2);
//LokiPoe.InGameState.SkillBarPanel.UseOn(_cycloneSlot, true, bestTarget);
//int auxDist = Loki.Game.LokiPoe.Random.Next(1, 5);
//LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, myPos.GetPointAtDistanceAfterThis(cachedPosition, auxDist));
//LokiPoe.InGameState.SkillBarPanel.UseAt(_cycloneSlot, true, cachedPosition);
Log.ErrorFormat("Attack at mob pos ");
}
}
else
{
if (_leapSlamSlot != -1)
{
LokiPoe.InGameState.SkillBarPanel.UseAt(_leapSlamSlot, true, cachedPosition);
Log.ErrorFormat("Leap Slam cuz distance -> {0} and targetrage -> {1} ", cachedDistance, cycloneTargetRange);
}
}
}
await DisableAlwaysHiglight();
return true;
}
return false;
}
#endregion
private int EnsurceCast(int slot)
{
if (slot == -1)
return slot;
var slotSkill = LokiPoe.InGameState.SkillBarPanel.Slot(slot);
if (slotSkill == null || !slotSkill.CanUse())
{
return -1;
}
return slot;
}
#region Override of Object
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Name + ": " + Description;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment