using D2Data; using D2Onion; using D2Packets; using D2Toolkit; using D2Toolkit.Units; using ETUtils; using GameClient; using GameServer; using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Windows.Forms; namespace D2Toolkit.Modules { public class GuardianEagle : D2Toolkit.IModule { private D2Toolkit.D2Instance d2; public bool Initialize(D2Toolkit.D2Instance d2) { this.d2 = d2; return true; } // - User code starts here (don't remove this line!) public enum GuardianAction { None = 0, Healing = 1, Mana = 2, Rejuvenation = 3, FullRejuvenation = 4, TownPortal = 5, Chicken = 6, } private GuardianConfig config; public class GuardianConfig { public GuardConfig Ping = new GuardConfig(); public GuardConfig Life = new GuardConfig(); public GuardConfig Mana = new GuardConfig(); public GuardConfig Merc = new GuardConfig(); public GuardianHotkeysConfig Hotkeys = new GuardianHotkeysConfig(); public GuardianDelaysConfig Delays = new GuardianDelaysConfig(); } public class GuardConfig { public bool Watch; public int Healing; public int Mana; public int Rejuvenation; public int FullRejuvenation; public int TownPortal; public int Chicken; public GuardianAction NoHealing; public GuardianAction NoMana; public GuardianAction NoRejuvenation; public GuardianAction NoFullRejuvenation; public GuardianAction NoTownPortal; public GuardianAction TownPortalTimeout; public GuardianAction Death; public PotionType HealingPotions = PotionType.AnyHealing; public PotionType ManaPotions = PotionType.AnyMana; public FindPotionFlags FindPotionFlags; public bool IsMerc; } public class GuardianHotkeysConfig { public GameSound Healing = GameSound.None; public GameSound Mana = GameSound.None; public GameSound Rejuvenation = GameSound.None; public GameSound FullRejuvenation = GameSound.None; public GameSound TownPortal = GameSound.None; public GameSound Chicken = GameSound.None; public GameSound ToogleActive = GameSound.None; public GuardConfig Guard = new GuardConfig(); } public class GuardianDelaysConfig { public TimeSpan AnyPotion = TimeSpan.FromMilliseconds(100); public TimeSpan Healing = TimeSpan.FromMilliseconds(300); public TimeSpan Mana = TimeSpan.FromMilliseconds(300); public TimeSpan Rejuvenation = TimeSpan.FromMilliseconds(500); public TimeSpan TakePortal = TimeSpan.Zero; public TimeSpan WaitForPortalTimeout = TimeSpan.FromMilliseconds(5000); public TimeSpan TakePortalTimeout = TimeSpan.FromMilliseconds(3000); } private PotionType ParsePotionType(string value) { PotionType retVal = PotionType.None; if (value == null || value.Length == 0) return retVal; string[] vals = StringUtils.RemoveWhiteSpace(value).Split(',', '|'); for (int i = 0; i < vals.Length; i++) retVal |= (PotionType)Enum.Parse(typeof(PotionType), vals[i], true); return retVal; } private Keys ParseKeys(string value) { Keys retVal = Keys.None; if (value == null || value.Length == 0) return retVal; string[] vals = StringUtils.RemoveWhiteSpace(value).Split(',', '|'); for (int i = 0; i < vals.Length; i++) retVal |= (Keys)Enum.Parse(typeof(Keys), vals[i], true); return retVal; } private TimeSpan ParseTimeSpan(string value, TimeSpan def) { if (value == null || value.Length == 0) return def; return TimeSpan.FromMilliseconds(Int32.Parse(value)); } private void LoadConfig(string configPath) { Nini.Config.IniConfigSource source = new Nini.Config.IniConfigSource(configPath); source.Alias.AddAlias("Yes", true); source.Alias.AddAlias("No", false); source.Alias.AddAlias("On", true); source.Alias.AddAlias("Off", false); source.Alias.AddAlias("True", true); source.Alias.AddAlias("False", false); source.CaseSensitive = false; this.config = new GuardianConfig(); Nini.Config.IConfig conf; if ((conf = source.Configs["Ping"]) != null) { this.config.Ping.Watch = conf.GetBoolean("Watch", false); if (conf.Contains("TownPortal")) this.config.Ping.TownPortal = conf.GetInt("TownPortal"); if (conf.Contains("Chicken")) this.config.Ping.Chicken = conf.GetInt("Chicken"); if (conf.Contains("NoTownPortal")) this.config.Ping.NoTownPortal = StringUtils.ParseEnum(conf.GetString("NoTownPortal")); if (conf.Contains("TownPortalTimeout")) this.config.Ping.NoTownPortal = StringUtils.ParseEnum(conf.GetString("TownPortalTimeout")); } else this.config.Ping.Watch = false; if ((conf = source.Configs["Life"]) != null) { this.config.Life.Watch = conf.GetBoolean("Watch", true); if (conf.Contains("Healing")) this.config.Life.Healing = conf.GetInt("Healing"); if (conf.Contains("Rejuvenation")) this.config.Life.Rejuvenation = conf.GetInt("Rejuvenation"); if (conf.Contains("FullRejuvenation")) this.config.Life.FullRejuvenation = conf.GetInt("FullRejuvenation"); if (conf.Contains("TownPortal")) this.config.Life.TownPortal = conf.GetInt("TownPortal"); if (conf.Contains("Chicken")) this.config.Life.Chicken = conf.GetInt("Chicken"); if (conf.Contains("NoHealing")) this.config.Life.NoHealing = StringUtils.ParseEnum(conf.GetString("NoHealing")); if (conf.Contains("NoRejuvenation")) this.config.Life.NoRejuvenation = StringUtils.ParseEnum(conf.GetString("NoRejuvenation")); if (conf.Contains("NoFullRejuvenation")) this.config.Life.NoFullRejuvenation = StringUtils.ParseEnum(conf.GetString("NoFullRejuvenation")); if (conf.Contains("NoTownPortal")) this.config.Life.NoTownPortal = StringUtils.ParseEnum(conf.GetString("NoTownPortal")); if (conf.Contains("TownPortalTimeout")) this.config.Life.NoTownPortal = StringUtils.ParseEnum(conf.GetString("TownPortalTimeout")); if (conf.Contains("HealingPotions")) this.config.Life.HealingPotions = (PotionType)StringUtils.ParseEnumFlags( conf.GetString("HealingPotions"), PotionType.AnyHealing); if (conf.Contains("SmallestFirst") && conf.GetBoolean("SmallestFirst")) this.config.Life.FindPotionFlags |= FindPotionFlags.SmallestFirst; if (!conf.Contains("FromBelt") || conf.GetBoolean("FromBelt")) this.config.Life.FindPotionFlags |= FindPotionFlags.FromBelt; if (conf.Contains("FromInventory") && conf.GetBoolean("FromInventory")) this.config.Life.FindPotionFlags |= FindPotionFlags.FromInventory; if (conf.Contains("FromBeltFirst") && conf.GetBoolean("FromBeltFirst")) this.config.Life.FindPotionFlags |= FindPotionFlags.FromBeltFirst; } else this.config.Life.Watch = false; if ((conf = source.Configs["Mana"]) != null) { this.config.Mana.Watch = conf.GetBoolean("Watch", false); if (conf.Contains("Mana")) this.config.Mana.Mana = conf.GetInt("Mana"); if (conf.Contains("Rejuvenation")) this.config.Mana.Rejuvenation = conf.GetInt("Rejuvenation"); if (conf.Contains("FullRejuvenation")) this.config.Mana.FullRejuvenation = conf.GetInt("FullRejuvenation"); if (conf.Contains("TownPortal")) this.config.Mana.TownPortal = conf.GetInt("TownPortal"); if (conf.Contains("Chicken")) this.config.Mana.Chicken = conf.GetInt("Chicken"); if (conf.Contains("NoMana")) this.config.Mana.NoMana = StringUtils.ParseEnum(conf.GetString("NoMana")); if (conf.Contains("NoRejuvenation")) this.config.Mana.NoRejuvenation = StringUtils.ParseEnum(conf.GetString("NoRejuvenation")); if (conf.Contains("NoFullRejuvenation")) this.config.Mana.NoFullRejuvenation = StringUtils.ParseEnum(conf.GetString("NoFullRejuvenation")); if (conf.Contains("NoTownPortal")) this.config.Mana.NoTownPortal = StringUtils.ParseEnum(conf.GetString("NoTownPortal")); if (conf.Contains("TownPortalTimeout")) this.config.Mana.NoTownPortal = StringUtils.ParseEnum(conf.GetString("TownPortalTimeout")); if (conf.Contains("ManaPotions")) this.config.Life.ManaPotions = (PotionType)StringUtils.ParseEnumFlags( conf.GetString("ManaPotions"), PotionType.AnyMana); if (conf.Contains("SmallestFirst") && conf.GetBoolean("SmallestFirst")) this.config.Mana.FindPotionFlags |= FindPotionFlags.SmallestFirst; if (!conf.Contains("FromBelt") || conf.GetBoolean("FromBelt")) this.config.Mana.FindPotionFlags |= FindPotionFlags.FromBelt; if (conf.Contains("FromInventory") && conf.GetBoolean("FromInventory")) this.config.Mana.FindPotionFlags |= FindPotionFlags.FromInventory; if (conf.Contains("FromBeltFirst") && conf.GetBoolean("FromBeltFirst")) this.config.Mana.FindPotionFlags |= FindPotionFlags.FromBeltFirst; } else this.config.Mana.Watch = false; if ((conf = source.Configs["Merc"]) != null) { this.config.Merc.Watch = conf.GetBoolean("Watch", false); if (conf.Contains("Healing")) this.config.Merc.Healing = conf.GetInt("Healing"); if (conf.Contains("Rejuvenation")) this.config.Merc.Rejuvenation = conf.GetInt("Rejuvenation"); if (conf.Contains("FullRejuvenation")) this.config.Merc.FullRejuvenation = conf.GetInt("FullRejuvenation"); if (conf.Contains("TownPortal")) this.config.Merc.TownPortal = conf.GetInt("TownPortal"); if (conf.Contains("Chicken")) this.config.Merc.Chicken = conf.GetInt("Chicken"); if (conf.Contains("NoHealing")) this.config.Merc.NoHealing = StringUtils.ParseEnum(conf.GetString("NoHealing")); if (conf.Contains("NoRejuvenation")) this.config.Merc.NoRejuvenation = StringUtils.ParseEnum(conf.GetString("NoRejuvenation")); if (conf.Contains("NoFullRejuvenation")) this.config.Merc.NoFullRejuvenation = StringUtils.ParseEnum(conf.GetString("NoFullRejuvenation")); if (conf.Contains("NoTownPortal")) this.config.Merc.NoTownPortal = StringUtils.ParseEnum(conf.GetString("NoTownPortal")); if (conf.Contains("TownPortalTimeout")) this.config.Merc.NoTownPortal = StringUtils.ParseEnum(conf.GetString("TownPortalTimeout")); if (conf.Contains("Death")) this.config.Merc.Death = StringUtils.ParseEnum(conf.GetString("Death")); if (conf.Contains("SmallestFirst") && conf.GetBoolean("SmallestFirst")) this.config.Merc.FindPotionFlags |= FindPotionFlags.SmallestFirst; this.config.Merc.FindPotionFlags |= FindPotionFlags.FromBelt; this.config.Merc.IsMerc = true; } else this.config.Merc.Watch = false; if ((conf = source.Configs["Hotkeys"]) != null) { if (conf.Contains("ToogleActive")) this.config.Hotkeys.ToogleActive = StringUtils.ParseEnum(conf.GetString("ToogleActive"), GameSound.None); if (conf.Contains("Chicken")) this.config.Hotkeys.Chicken = StringUtils.ParseEnum(conf.GetString("Chicken"), GameSound.None); if (conf.Contains("TownPortal")) this.config.Hotkeys.TownPortal = StringUtils.ParseEnum(conf.GetString("TownPortal"), GameSound.None); if (conf.Contains("NoTownPortal")) this.config.Hotkeys.Guard.NoTownPortal = StringUtils.ParseEnum(conf.GetString("NoTownPortal")); if (conf.Contains("TownPortalTimeout")) this.config.Hotkeys.Guard.NoTownPortal = StringUtils.ParseEnum(conf.GetString("TownPortalTimeout")); } if ((conf = source.Configs["Delays"]) != null) { if (conf.Contains("AnyPotion")) this.config.Delays.AnyPotion = ParseTimeSpan(conf.GetString("AnyPotion"), this.config.Delays.AnyPotion); if (conf.Contains("Healing")) this.config.Delays.Healing = ParseTimeSpan(conf.GetString("Healing"), this.config.Delays.Healing); if (conf.Contains("Mana")) this.config.Delays.Mana = ParseTimeSpan(conf.GetString("Mana"), this.config.Delays.Mana); if (conf.Contains("Rejuvenation")) this.config.Delays.Rejuvenation = ParseTimeSpan(conf.GetString("Rejuvenation"), this.config.Delays.Rejuvenation); if (conf.Contains("TakePortal")) this.config.Delays.TakePortal = ParseTimeSpan(conf.GetString("TakePortal"), this.config.Delays.TakePortal); if (conf.Contains("WaitForPortalTimeout")) this.config.Delays.WaitForPortalTimeout = ParseTimeSpan(conf.GetString("WaitForPortalTimeout"), this.config.Delays.WaitForPortalTimeout); if (conf.Contains("TakePortalTimeout")) this.config.Delays.TakePortalTimeout = ParseTimeSpan(conf.GetString("TakePortalTimeout"), this.config.Delays.TakePortalTimeout); } } private bool active = true; private bool tping; private DateTime anyPotionTimer; private DateTime healingTimer; private DateTime manaTimer; private DateTime rejuvenationTimer; public void Main() { this.anyPotionTimer = DateTime.Now - TimeSpan.FromDays(1); this.healingTimer = this.manaTimer = this.rejuvenationTimer = this.anyPotionTimer; string configPath = d2.ConfigPath + Path.DirectorySeparatorChar + "GuardianEagle.ini"; if (!File.Exists(configPath)) { d2.Output("GuardianEagle: could not find configuration file; aborting..."); return; } this.LoadConfig(configPath); this.active = this.d2.Game != null; if (this.active) { d2.Game.Me.OnAssignMerc += this.onAssignMerc; if (d2.Game.Me.Merc != null) d2.Game.Me.Merc.OnLifeChange += this.onMercLifeChange; } d2.AddInterceptor(GameServerPacket.GameHandshake, this.onGameHandshake); d2.AddInterceptor(GameClientPacket.SendCharacterSpeech, this.onSendCharacterSpeech); d2.AddListener(GameServerPacket.LifeManaChange, this.onLifeManaChange); d2.OnConnectionStateChange += new ConnectionStateChangeHandler(this.onConnectionStateChange); d2.WaitForConnection(ConnectionKind.Bnet, ConnectionState.Disconnected); } public void Dispose() { d2.RemoveInterceptor(GameServerPacket.GameHandshake, this.onGameHandshake); d2.RemoveInterceptor(GameClientPacket.SendCharacterSpeech, this.onSendCharacterSpeech); d2.RemoveListener(GameServerPacket.LifeManaChange, this.onLifeManaChange); d2.OnConnectionStateChange -= new ConnectionStateChangeHandler(this.onConnectionStateChange); } private void onConnectionStateChange(object source, ConnectionStateChangeArgs e) { if (e.Connection == ConnectionKind.Game && e.State == ConnectionState.Disconnected) { this.active = false; } } private void onGameHandshake(D2Packet d2p) { if (!this.active) d2.Game.Me.OnAssignMerc += this.onAssignMerc; this.active = true; } private void onAssignMerc(Me me, NPC merc) { merc.OnLifeChange += this.onMercLifeChange; } private void onSendCharacterSpeech(D2Packet d2p) { GameClient.SendCharacterSpeech p = d2p as GameClient.SendCharacterSpeech; if (p.Speech == config.Hotkeys.ToogleActive) { d2.Output(this.active ? "Pausing GuardianEagle..." : "Resuming GuardianEagle..."); this.active = !this.active; } else if (p.Speech == config.Hotkeys.Chicken) { new Thread(this.hotkeyThread).Start(true); } else if (p.Speech == config.Hotkeys.TownPortal) { if (d2.Game.InTown) d2.Output("Cannot TP while in town!"); else new Thread(this.hotkeyThread).Start(false); } else return; p.Block(); } private void hotkeyThread(object isChicken) { this.Fly((bool)isChicken ? GuardianAction.Chicken : GuardianAction.TownPortal, config.Hotkeys.Guard, "Hotkey"); } private void onMercLifeChange(PlayerNPCBase merc) { if (!this.active || d2.Game.InTown || this.tping) return; if (!config.Merc.Watch) return; if (merc.Life < 1) { this.Fly(config.Merc.Death, config.Merc, "Death"); return; } int life = merc.LifePercent; if (config.Merc.Chicken > 0 && life < config.Merc.Chicken) this.Fly(GuardianAction.Chicken, config.Merc, "Life (" + life + ") < " + config.Merc.Chicken); else if (config.Merc.TownPortal > 0 && life < config.Merc.TownPortal) this.Fly(GuardianAction.TownPortal, config.Merc, "Life (" + life + ") < " + config.Merc.TownPortal); else { DateTime now = DateTime.Now; if (now - anyPotionTimer < config.Delays.AnyPotion) return; if (now - rejuvenationTimer > config.Delays.Rejuvenation) { if (config.Merc.FullRejuvenation > 0 && life < config.Merc.FullRejuvenation) { this.Fly(GuardianAction.FullRejuvenation, config.Merc, "Life (" + life + ") < " + config.Merc.FullRejuvenation); return; } else if (config.Merc.Rejuvenation > 0 && life < config.Merc.Rejuvenation) { this.Fly(GuardianAction.Rejuvenation, config.Merc, "Life (" + life + ") < " + config.Merc.Rejuvenation); return; } } if (now - healingTimer > config.Delays.Healing) { if (config.Merc.Healing > 0 && life < config.Merc.Healing) { this.Fly(GuardianAction.Healing, config.Merc, "Life (" + life + ") < " + config.Merc.Healing); return; } } } } private void onLifeManaChange(D2Packet d2p) { if (!this.active || d2.Game.InTown || this.tping) return; int life = d2.Game.Me.LifePercent; int mana = d2.Game.Me.ManaPercent; if (config.Life.Watch && config.Life.Chicken > 0 && life < config.Life.Chicken) this.Fly(GuardianAction.Chicken, config.Life, "Life (" + life + ") < " + config.Life.Chicken); else if (config.Mana.Watch && config.Mana.Chicken > 0 && mana < config.Mana.Chicken) this.Fly(GuardianAction.Chicken, config.Mana, "Mana (" + mana + ") < " + config.Mana.Chicken); else if (config.Life.Watch && config.Life.TownPortal > 0 && life < config.Life.TownPortal) this.Fly(GuardianAction.TownPortal, config.Life, "Life (" + life + ") < " + config.Life.TownPortal); else if (config.Mana.Watch && config.Mana.TownPortal > 0 && mana < config.Mana.TownPortal) this.Fly(GuardianAction.TownPortal, config.Mana, "Mana (" + mana + ") < " + config.Mana.TownPortal); else { DateTime now = DateTime.Now; if (now - anyPotionTimer < config.Delays.AnyPotion) return; if (now - rejuvenationTimer > config.Delays.Rejuvenation) { if (config.Life.Watch && config.Life.FullRejuvenation > 0 && life < config.Life.FullRejuvenation) { this.Fly(GuardianAction.FullRejuvenation, config.Life, "Life (" + life + ") < " + config.Life.FullRejuvenation); return; } else if (config.Mana.Watch && config.Mana.FullRejuvenation > 0 && mana < config.Mana.FullRejuvenation) { this.Fly(GuardianAction.FullRejuvenation, config.Mana, "Mana (" + mana + ") < " + config.Mana.FullRejuvenation); return; } else if (config.Life.Watch && config.Life.Rejuvenation > 0 && life < config.Life.Rejuvenation) { this.Fly(GuardianAction.Rejuvenation, config.Life, "Life (" + life + ") < " + config.Life.Rejuvenation); return; } else if (config.Mana.Watch && config.Mana.Rejuvenation > 0 && mana < config.Mana.Rejuvenation) { this.Fly(GuardianAction.Rejuvenation, config.Mana, "Mana (" + mana + ") < " + config.Mana.Rejuvenation); return; } } if (now - healingTimer > config.Delays.Healing) { if (config.Life.Watch && config.Life.Healing > 0 && life < config.Life.Healing) { this.Fly(GuardianAction.Healing, config.Life, "Life (" + life + ") < " + config.Life.Healing); return; } } if (now - manaTimer > config.Delays.Mana) { if (config.Mana.Watch && config.Mana.Mana > 0 && mana < config.Mana.Mana) { this.Fly(GuardianAction.Mana, config.Mana, "Mana (" + mana + ") < " + config.Mana.Mana); return; } } } } private void Fly(GuardianAction action, GuardConfig g, string source) { if (action == GuardianAction.None) return; d2.Output("Fly(): ", action, " : ", (g.IsMerc ? "Merc " : ""), source); switch (action) { case GuardianAction.Chicken: d2.Output("Chickening!"); d2.Game.Exit(); break; case GuardianAction.TownPortal: if (!d2.Game.Me.CastTP()) { d2.Output("Out of TPs!"); this.Fly(g.NoTownPortal, g, "Fallback"); } else { d2.Output("Screw you guys, I'm going home..."); if (!this.TownTP()) this.Fly(g.TownPortalTimeout, g, "Fallback"); } break; case GuardianAction.FullRejuvenation: if (!this.d2.Game.Me.DrinkPotion(PotionType.FullRejuvenation, g.IsMerc, g.FindPotionFlags)) this.Fly(g.NoFullRejuvenation, g, "Fallback"); else { anyPotionTimer = rejuvenationTimer = DateTime.Now; d2.Output("Drank a Full Rejuvenation potion."); } break; case GuardianAction.Rejuvenation: if (!this.d2.Game.Me.DrinkPotion(PotionType.Rejuvenation, g.IsMerc, g.FindPotionFlags)) this.Fly(g.NoRejuvenation, g, "Fallback"); else { anyPotionTimer = rejuvenationTimer = DateTime.Now; d2.Output("Drank a Rejuvenation potion."); } break; case GuardianAction.Healing: if (!this.d2.Game.Me.DrinkPotion(g.HealingPotions, g.IsMerc, g.FindPotionFlags)) this.Fly(g.NoHealing, g, "Fallback"); else { anyPotionTimer = healingTimer = DateTime.Now; d2.Output("Drank a Healing potion."); } break; case GuardianAction.Mana: if (!this.d2.Game.Me.DrinkPotion(g.ManaPotions, g.IsMerc, g.FindPotionFlags)) this.Fly(g.NoMana, g, "Fallback"); else { anyPotionTimer = manaTimer = DateTime.Now; d2.Output("Drank a Mana potion."); } break; } } public bool TownTP() { this.tping = true; DateTime start = DateTime.Now; while (true) { if (this.d2.Game.Me.TownPortal != null) break; if (DateTime.Now - start > this.config.Delays.WaitForPortalTimeout) { d2.Output("Timed out out while waiting for TP..."); this.tping = false; return false; } Thread.Sleep(1); } if (this.config.Delays.TakePortal > TimeSpan.Zero) Thread.Sleep(this.config.Delays.TakePortal); d2.SendToGameServer(GameClient.UnitInteract.Build( UnitType.GameObject, this.d2.Game.Me.TownPortal.CurrentSide.UID)); start = DateTime.Now; while (true) { if (d2.Game.Me.States.Contains(StateType.JustPortaled)) { this.tping = false; return true; } if (DateTime.Now - start > this.config.Delays.TakePortalTimeout) { d2.Output("Timed out out while taking TP..."); this.tping = false; return false; } Thread.Sleep(1); } } // - User code ends here (don't remove this line!) } }