Jump to content

Force to Moonglade on "ToTown" state.


Recommended Posts

I'd like to force my Druid bot to use Teleport: Moonglade, restock/repair and train skills there and then use HS when all of those activities have been done. If possible, I'd also like to disable the ToTown state if HS is on cooldown. Is this possible?

Link to comment
Share on other sites

Honestly cool idea. This could be a plugin, but it's more complicated than it seems to achieve. I don't think anyone wil volunteer to do that for you.

Link to comment
Share on other sites

Yep, that would do it. The hard part is "and then use HS when all of those activities have been done".

I guess you could inject a "Hearhstone" state with a lower prio than ToTown/Train etc.. which would run if your current location is Moonglade.

Link to comment
Share on other sites

I believe you can just let the state run that originally triggered your event handler (ToTown, most likely). It should know vendors in Moonglade, if your database is correctly filled.

If you really needed to, you could just create an instance of ToTown and Training in your plugin and run those manually. The run method should be blocking and as long as you're in Moonglade, there shouldn't be anything interrupting the state so you don't need (much) extra logic to figure out if it was successfully run.

Link to comment
Share on other sites

  • 3 weeks later...

In case anyone comes across this, I managed to achieve what I was looking to do using the below code;

    private void TrainingEvents()
    {
        robotManager.Events.FiniteStateMachineEvents.OnBeforeCheckIfNeedToRunState += (engine, state, cancelable) => 
        {
            var currentLevel = wManager.Wow.ObjectManager.ObjectManager.Me.Level;

            if (state.GetType() == typeof(Trainers))
            {   
                if (!wManager.wManagerSetting.CurrentSetting.TrainNewSkills || currentLevel%2 != 0 || (trainInMoonglade && !CanTravelToMoonglade())) 
                {
                    cancelable.Cancel = true;
                }
            }
        };

        robotManager.Events.FiniteStateMachineEvents.OnRunState += (engine, state, cancelable) => 
        {
            if (state.GetType() == typeof(Trainers) && wManager.Wow.Helpers.Usefuls.MapZoneName != "Moonglade" && trainInMoonglade && CanTravelToMoonglade())
            {   
                Lua.RunMacroText("/cast Teleport: Moonglade");
                wManager.Wow.Helpers.Usefuls.WaitIsCasting();
                Thread.Sleep(5000);
                if(wManager.Wow.Helpers.Usefuls.MapZoneName != "Moonglade")
                {
                    cancelable.Cancel = true;
                }
            }
        };

        robotManager.Events.FiniteStateMachineEvents.OnAfterRunState += (engine, state) => 
        {
            if (state.GetType() == typeof(Trainers))
            {   
                if (wManager.Wow.Helpers.Usefuls.MapZoneName == "Moonglade" && CanTravelToMoonglade())
                {
                    wManager.Wow.Helpers.ItemsManager.UseItem(6948);
                    wManager.Wow.Helpers.Usefuls.WaitIsCasting();
                    Thread.Sleep(5000);
                    wManager.Wow.Bot.States.ToTown.ForceToTown = true;
                }
            }
        };
    }

    private void AddDruidTrainer(string Name, int Id, double x, double y, double z)
    {
        AddNpc(Name, Id, x, y, z, wManager.Wow.Class.Npc.NpcType.DruidTrainer);
        wManager.wManagerSetting.CurrentSetting.TrainNewSkills = true;
    }

    private void TrainInMoonglade()
    {
        AddDruidTrainer("Loganaar", 12042, 7867.13, -2593.73, 486.8367);
        trainInMoonglade = true;
    }

    private bool CanTravelToMoonglade()
    {
        return wManager.Wow.Helpers.SpellManager.KnowSpell(18960) && Lua.LuaDoString<bool>("return GetItemCooldown(6948)==0");
    }

    private void AddNpc(string Name, int Id, double x, double y, double z, wManager.Wow.Class.Npc.NpcType Type)
    {
        robotManager.Helpful.Vector3 pos = new robotManager.Helpful.Vector3(x, y, z);
        wManager.Wow.Class.Npc npc = new wManager.Wow.Class.Npc 
        {
            Type = Type,
            Name = Name,
            Entry = Id,
            Position = pos,
            ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
            Faction = wManager.Wow.Class.Npc.FactionType.Neutral,
            CanFlyTo = true
        };
        wManager.Wow.Helpers.NpcDB.AddNpc(npc, false, true);
    }

The plugin I'm working on does a lot more stuff so there may be methods in the snippet above that seem redundant but they're used for wider purposes in the overall plugin.

I reworked what I originally planned to do slightly as there isn't a mailbox in Moonglade, so the bot will teleport to Moonglade to train, then HS, then run ToTown on return.

I'd imagine this could be expanded to mages to TP to Org etc too - I'll probably also look at adding some checks to HS if in Moonglade on non-Trainer events too (in case the bot crashes/closes there and then the bot doesn't know how to get back other than running...)

Next up is to figure out how to make it only learn skills I want instead of all of them if anyone knows where I should start there?

Link to comment
Share on other sites

Hey, I cleaned up your method and make it look a little cleaner. when i get a chance i will do some editing and see what i can do to help you improve it.

 

    public class Moonglade
    {
        static Moonglade()
        {
            Npc npc = new Npc
            {
                Type = NpcType.DruidTrainer,
                Name = "Loganaar",
                Entry = 12042,
                Position = new Vector3(7867.13, -2593.73, 486.8367),
                ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
                Faction = Npc.FactionType.Neutral,
                CanFlyTo = true
            };
            NpcDB.AddNpc(npc, true, true);
            Logging.Write($"[Moonglade]: Trainer {npc.Name} Was Added to Npc Database");
        }
        internal static void Start()
        {
            FiniteStateMachineEvents.OnBeforeCheckIfNeedToRunState += OnRunState;
            FiniteStateMachineEvents.OnRunState += OnRunState;
            FiniteStateMachineEvents.OnAfterRunState += OnAfterRunState;
        }

        internal static void Dispose()
        {
            FiniteStateMachineEvents.OnRunState -= OnRunState; 
            FiniteStateMachineEvents.OnBeforeCheckIfNeedToRunState -= OnRunState;
            FiniteStateMachineEvents.OnAfterRunState -= OnAfterRunState;
        }

        private static void OnRunState(Engine Engine, State state, CancelEventArgs cancelable)
        {
            if (state.GetType() != typeof(Trainers)) return;

            if (CurrentLevel % 2 != 0 || !CanTravel || !wManager.wManagerSetting.CurrentSetting.TrainNewSkills) return;

            if (!MapName.Contains("Moonglade"))
            {
                cancelable.Cancel = true;
                MovementManager.StopMoveTo(true, Random);
                Lua.RunMacroText("/cast Teleport: Moonglade");
                Usefuls.WaitIsCasting();
                Thread.Sleep(Random);
            }
        }

        private static void OnAfterRunState(Engine Engine, State state)
        {
            if (state.GetType() != typeof(Trainers) || !CanTravel) return;

            if (MapName.Contains("Moonglade"))
            {
                ItemsManager.UseItem(6948);
                Usefuls.WaitIsCasting();
                Thread.Sleep(5000);
                ToTown.ForceToTown = true;
            }
        }

       
        private static bool CanTravel
        {
            get
            {
                return SpellManager.KnowSpell(18960) && GetItemCoolDown(6948) == 0;
            }
        }
   
        private static String MapName => Usefuls.MapZoneName;
        private static Int32 Random = Others.Random(4000, 7000);
        private static ulong CurrentLevel => ObjectManager.Me.Level;

        private static Int32 GetItemCoolDown(Int32 ItemID)
        {
            return Lua.LuaDoString<Int32>($@"local startTime, duration, _ = GetItemCooldown({ItemID});
                                                   return duration - (GetTime() - startTime);");
        }
    }

 

Link to comment
Share on other sites

I'll look at implementing some of the cleaner methods, but some of them would lead to a lot of duplicated code in the full plugin. Primarily the removal of AddNpc() as a lot more than just the moonglade trainer are managed by it.

 

Thanks.

Link to comment
Share on other sites

Readability is my preference, my plugin currently manages over 150 NPCs, adding them/quickly seeing what the code is doing is far easier in my version;

AddRepair("Kennah Hawkseye", 3078, -2275.53, -289.265, -9.42487);
AddVendor("Jhawna Oatwind", 3884, -2378.96, -399.268, -3.889035);
AddMailbox("Mailbox", 143984, -2338.21, -367.143, -8.52861);
AddDruidTrainer("Gennia Runetotem", 3064, -2315.75, -442.634, -5.438329);

vs

Npc npc = new Npc
{
    Type = NpcType.Vendor,
    Name = "Kennah Hawkseye",
    Entry = 3078,
    Position = new Vector3(-2275.53, -289.265, -9.42487),
    ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
    Faction = Npc.FactionType.Neutral,
    CanFlyTo = true
};
NpcDB.AddNpc(npc, false, true);

npc = new Npc
{
    Type = NpcType.Repair,
    Name = "Jhawna Oatwind",
    Entry = 3884,
    Position = new Vector3(-2378.96, -399.268, -3.889035),
    ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
    Faction = Npc.FactionType.Neutral,
    CanFlyTo = true
};
NpcDB.AddNpc(npc, false, true);

npc = new Npc
{
    Type = NpcType.Mailbox,
    Name = "Mailbox",
    Entry = 143984,
    Position = new Vector3(-2338.21, -367.143, -8.52861),
    ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
    Faction = Npc.FactionType.Neutral,
    CanFlyTo = true
};
NpcDB.AddNpc(npc, false, true);

npc = new Npc
{
    Type = NpcType.DruidTrainer,
    Name = "Gennia Runetotem",
    Entry = 3064,
    Position = new Vector3(-2315.75, -442.634, -5.438329),
    ContinentId = wManager.Wow.Enums.ContinentId.Kalimdor,
    Faction = Npc.FactionType.Neutral,
    CanFlyTo = true
};
NpcDB.AddNpc(npc, false, true);

I also noticed you changed the "save" bool to store in the NpcDb to true, it's intentionally set to false.

Link to comment
Share on other sites

Just wanted to chip in and say you absolutely shouldn't store NPCs in the database. Temporarily adding them (unless already available) is fine. But if you save them, you'll eventually give WRobot too much info that it doesn't need. It will go to the wrong NPCs/trainers just because they're closer in 2D space. 

Back in the day before I wrote my own states and completely got rid of WRobot's states, I would intercept the ToTown state, clear the NPC DB and just inject my own based on 3D distance from a pre-select NPC list.
WRobot will do a lot of dumb things if you give it free reign. Having a ton of NPCs available is just asking it to make the wrong choices. 

Intercepting states and forcing selection is absolutely the way to go. I know people who wrote specific questers/grinders for their operations that would force inject a food vendor and repair for every level section and overwrite them with every "zone" change. Same for trainers, pretty much. 

Edited by Matenia
Link to comment
Share on other sites

34 minutes ago, Matenia said:

Just wanted to chip in and say you absolutely shouldn't store NPCs in the database. Temporarily adding them (unless already available) is fine. But if you save them, you'll eventually give WRobot too much info that it doesn't need. It will go to the wrong NPCs/trainers just because they're closer in 2D space. 

Back in the day before I wrote my own states and completely got rid of WRobot's states, I would intercept the ToTown state, clear the NPC DB and just inject my own based on 3D distance from a pre-select NPC list.
WRobot will do a lot of dumb things if you give it free reign. Having a ton of NPCs available is just asking it to make the wrong choices. 

Intercepting states and forcing selection is absolutely the way to go. I know people who wrote specific questers/grinders for their operations that would force inject a food vendor and repair for every level section and overwrite them with every "zone" change. Same for trainers, pretty much. 

That's exactly what I'm doing! I got sick of the bot trying to mission to NPCs way out of its level range and getting stuck on a never ending death loop. So I don't let it know about any NPCs, I clear the NPC DB on every to town state and explicitly tell it which NPCs to use based on level and zone. Sample below;

    private void MulgoreRedCloud()
    {
        if (IsZoneLevel("Mulgore", 0, 6))
        {
            SetFoodDrink("Tough Hunk of Bread", "Refreshing Spring Water", 16, 36);

            AddVendor("Kawnie Softbreeze", 3072, -2893.72, -279.332, 53.91661);
            
            AddDruidTrainer("Gart Mistrunner", 3060, -2873.57, -268.591, -53.91648);
        }
    }

    private void MulgoreBloodhoof()
    {
        if (IsZoneLevel("Mulgore", 7, 12))
        {
            SellFoodDrink("Tough Hunk of Bread", "Refreshing Spring Water");
            SetFoodDrink("Freshly Baked Bread", "Ice Cold Milk");

            AddRepair("Kennah Hawkseye", 3078, -2275.53, -289.265, -9.42487);
            AddVendor("Jhawna Oatwind", 3884, -2378.96, -399.268, -3.889035);
            AddMailbox("Mailbox", 143984, -2338.21, -367.143, -8.52861);
            
            AddDruidTrainer("Gennia Runetotem", 3064, -2315.75, -442.634, -5.438329);
        }
    }

    private void BarrensCrossroads()
    {
        if (IsZoneLevel("The Barrens", 10, 19))
        {
            SellFoodDrink("Freshly Baked Bread");
            SetFoodDrink("Tel'Abim Banana", "Ice Cold Milk");

            AddRepair("Nargal Deatheye", 3479, -356.996, -2568.86, 95.78785);
            AddVendor("Innkeeper Boorand Plainswind", 3934, -407.123, -2645.22, 96.22299);
            AddMailbox("Mailbox", 143982, -443.692, -2649.08, 95.7738);
            
            TrainInMoonglade();
        }
    }

It takes a little longer to setup, but I've built the definitions around a questing profile I setup that takes me all the way to 60, combined with this I've had a bot get to 60 with only one death and minimal intervention - working on improving /played time and fixing some pathing issues now (there's areas the bot always gets stuck, but I've seen a code snippet where I can override path selection within a range of a vector).

Link to comment
Share on other sites

I actually think these threads are quite constructive, so I'm going to share some pathing code I use in HMP to hopefully help you out.

You can specifically add OffMesh connections. In fact, you can add them to be used EVEN if WRobot can still find a path. You can also store functions in WRobot's internal Vars, which can be referenced using the C# string that offmesh connections are capable of using. So you could even call them from a quester profile, as long as you save the function in that Var map first.

using System;
using System.Collections.Generic;
using System.Threading;
using robotManager.Helpful;
using wManager;
using wManager.Wow.Class;
using wManager.Wow.Enums;
using wManager.Wow.Helpers;
using wManager.Wow.ObjectManager;

public class OffMeshes
{
	public static readonly string PathToKey = "HMPPathToFunction";
	public static readonly string MoveForwardAndResetKey = "HMPMoveForwardAndReset";

	public static bool PathTo(Vector3 position)
	{
		var path = PathFinder.FindPath(position);
		foreach (var pos in path)
		{
			while (Conditions.InGameAndConnectedAndAliveAndProductStartedNotInPause && ObjectManager.Me.Position.DistanceTo2D(pos) > 1.5)
			{
				MovementManager.MoveTo(pos);
				Thread.Sleep(500);
			}
		}

		MovementManager.StopMove();
		return position.DistanceTo2D(ObjectManager.Me.Position) <= 1.5f;
	}

	public static void MoveForwardAndReset(int ms = 15000)
	{
		var temp = wManagerSetting.CurrentSetting.CloseIfPlayerTeleported;
		wManagerSetting.CurrentSetting.CloseIfPlayerTeleported = false;
		Move.Forward(Move.MoveAction.PressKey, ms);
		Thread.Sleep(ms);
		wManagerSetting.CurrentSetting.CloseIfPlayerTeleported = temp;
		if (ObjectManager.Me.GetMove)
		{
			MovementManager.StopMove();
		}
	}

	public static void AddConnections()
	{
		// Setting functions to WRobot's internal map to re-use them later in Offmesh connections
		Var.SetVar(PathToKey, new Action<Vector3>((x) => PathTo(x)));
		Var.SetVar(MoveForwardAndResetKey, new Action(() => MoveForwardAndReset()));

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(174.0891, 1185.221, 166.1563),
			                                  new Vector3(196.5771, 1180.41, 167.9987),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Maxxton"
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(-68.50166, 1186.978, 90.78709),
			                                  new Vector3(-12.31121, 1205.146, 109.4463),
			                                  new Vector3(24.1873, 1218.461, 133.6504),
			                                  new Vector3(63.54899, 1219.134, 154.2211),
			                                  new Vector3(153.3421, 1216.366, 165.8533),
			                                  new Vector3(176.0194, 1186.073, 166.0586),
			                                  new Vector3(198.7642, 1180.339, 167.9988),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Maxxton 2",
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(100.9536, 1223.328, 161.6412),
			                                  new Vector3(154.9091, 1209.382, 165.5027),
			                                  new Vector3(173.1814, 1190.673, 166.1338),
			                                  new Vector3(197.2656, 1180.434, 167.999),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Maxxton 3"
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(247.4097, 1253.189, 192.1647),
			                                  new Vector3(255.671, 1254.537, 192.1407),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Innkeeper"
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(254.4657, 1214.722, 189.7014),
			                                  new Vector3(248.5491, 1251.517, 192.1623),
			                                  new Vector3(255.7968, 1253.575, 192.1399),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Innkeeper2"
		                                  });


		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(247.4097, 1253.189, 192.1647),
			                                  new Vector3(255.1739, 1256.671, 192.1407),
		                                  }, 1)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
			                                  Name = "Nijel's Point - Janet Hommers"
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(-861.2993, -3758.589, 19.47886, "None"),
			                                  new Vector3(-849.3063, -3742.703, 22.49161, "None"),
			                                  new Vector3(-842.8103, -3734.129, 19.89155, "None"),
			                                  new Vector3(-849.7686, -3735.616, 19.88754, "None"),
			                                  new Vector3(-848.3646, -3731.851, 21.27056, "None"),
			                                  new Vector3(-843.2056, -3727.342, 25.23064, "None"),
			                                  new Vector3(-839.3514, -3726.285, 26.32008, "None")
		                                  }, (int) ContinentId.Kalimdor)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Bidirectional,
			                                  Name = "Ratchet - Gazlowe",
			                                  TryToUseEvenIfCanFindPathSuccess = true
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(1155.042, 182.7223, 3.134686, "None"),
			                                  new Vector3(1157.968, 184.0907, 5.895848, "None"),
			                                  new Vector3(1157.852, 185.0874, 6.79552, "None"),
			                                  new Vector3(1169.146, 188.5145, 17.70432, "None"),
			                                  new Vector3(1176.177, 183.1431, 21.6543, "None"),
			                                  new Vector3(1174.764, 180.1774, 21.44643, "None"),
			                                  new Vector3(1170.609, 182.9803, 23.67873, "None"),
			                                  new Vector3(1167.625, 185.7788, 27.12425, "None"),
			                                  new Vector3(1165.26, 188.2767, 28.77732, "None"),
			                                  new Vector3(1161.465, 183.8654, 29.03481, "None"),
			                                  new Vector3(1164.393, 176.3627, 31.69842, "None")
		                                  }, (int) ContinentId.Kalimdor)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Bidirectional,
			                                  Name = "Stonetalon - Big machine",
			                                  TryToUseEvenIfCanFindPathSuccess = true
		                                  });

		PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
		                                  {
			                                  new Vector3(-9009.081, 851.679, 105.893, "None"),
			                                  new Vector3(-9017.271, 864.8356, 109.8218, "None"),
			                                  new Vector3(-9014.967, 882.9603, 112.9219, "None"),
			                                  new Vector3(-8999.138, 890.7767, 115.9936, "None"),
			                                  new Vector3(-8982.68, 880.2233, 119.937, "None"),
			                                  new Vector3(-8982.612, 866.5609, 123.3268, "None"),
			                                  new Vector3(-8992.165, 860.2961, 126.6532, "None"),
			                                  new Vector3(-9001.312, 864.4201, 129.772, "None"),
			                                  new Vector3(-9013.052, 873.2303, 132.0581, "None"),
			                                  new Vector3(-9007.667, 877.3668, 135.9138, "None"),
			                                  new Vector3(-9003.198, 874.1686, 139.3298, "None"),
			                                  new Vector3(-9004.052, 868.6579, 142.6572, "None"),
			                                  new Vector3(-9009.047, 867.0392, 145.7849, "None"),
			                                  new Vector3(-9013.107, 871.0745, 148.6166, "None"),
			                                  new Vector3(-9016.262, 884.7032, 29.6207, "None")
		                                  }, (int) ContinentId.Azeroth)
		                                  {
			                                  Type = PathFinder.OffMeshConnectionType.Bidirectional,
			                                  Name = "SW Mage Tower",
			                                  TryToUseEvenIfCanFindPathSuccess = true
		                                  });
	}
}

 

Here is an example of how to use Darnassus connections. It teaches WRobot that the portals and ships are just regular pathing connections.

using System;
using System.Collections.Generic;
using robotManager.Helpful;
using wManager;
using wManager.Wow.Enums;
using wManager.Wow.Helpers;

namespace HumanMasterPlugin.Pathing
{
	public static class Darnassus
	{
		public static readonly string ToAuberdineKey = "HMPToAuberdine";
		public static readonly string ToRuthEranKey = "HMPToRuthEran";
		
		private static readonly Ship _ship = Helper.RealWowVersion > 5875 ? DarnassusAuberdineBoatTBC.Ship : DarnassusAuberdineBoatVanilla.Ship;

		public static void Start()
		{
			wManagerSetting.CurrentSetting.OffMeshConnectionsSearchDistance = 1000;
			
			//blacklist natural darnassus path
			PathFinder.ReportBigDangerArea(new Vector3(9161.415, 1125.652, 1304.879), 100f);

			Var.SetVar(ToRuthEranKey, new Action(ToRuthEran));
			Var.SetVar(ToAuberdineKey, new Action(ToAuberdine));

			var moveforwardAction = $@"c#: robotManager.Helpful.Var.GetVar<Action>(""{OffMeshes.MoveForwardAndResetKey}"")();";
			var auberdineAction = $@"c#: robotManager.Helpful.Var.GetVar<Action>(""{ToAuberdineKey}"")();";
			var ruthEranAction = $@"c#: robotManager.Helpful.Var.GetVar<Action>(""{ToRuthEranKey}"")();";
			
			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(9946.901, 2556.688, 1316.229, "None"),
				                                  new Vector3(9946.558, 2576.157, 1318.328, "None"),
				                                  new Vector3(9946.107, 2594.624, 1316.194, "None"),
				                                  new Vector3(9946.401, 2617.684, 1316.665, "None") {Action = moveforwardAction},
				                                  new Vector3(8783.707, 966.4249, 30.20752, "None"),
				                                  new Vector3(8774.904, 964.0229, 30.35609, "None"),
				                                  new Vector3(8755.102, 952.9327, 26.45088, "None"),
				                                  new Vector3(8726.162, 942.1434, 17.12174, "None"),
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Darnassus Portal Down",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });
			
			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(8672.928, 985.4261, 6.262514, "None"),
				                                  new Vector3(8645.084, 997.5281, 4.820365, "None"),
				                                  new Vector3(8615.801, 1007.996, 5.570138, "None"),
				                                  new Vector3(8557.271, 1020.589, 5.375874, "None"),
				                                  new Vector3(8554.27, 1021.136, 5.495627, "None") {Action = auberdineAction},
				                                  new Vector3(6557.808, 774.058, 6.835019, "None"),
				                                  new Vector3(6564.013, 773.3265, 6.590591, "None"),
				                                  new Vector3(6551.588, 777.1901, 6.788247, "None")
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Rut'Theran to Auberdine",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });

			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(9946.901, 2556.688, 1316.229, "None"),
				                                  new Vector3(9946.558, 2576.157, 1318.328, "None"),
				                                  new Vector3(9946.107, 2594.624, 1316.194, "None"),
				                                  new Vector3(9946.401, 2617.684, 1316.665, "None") {Action = moveforwardAction},
				                                  new Vector3(8783.707, 966.4249, 30.20752, "None"),
				                                  new Vector3(8774.904, 964.0229, 30.35609, "None"),
				                                  new Vector3(8755.102, 952.9327, 26.45088, "None"),
				                                  new Vector3(8726.162, 942.1434, 17.12174, "None"),
				                                  new Vector3(8554.27, 1021.136, 5.495627, "None") {Action = auberdineAction},
				                                  new Vector3(6577.144, 767.6254, 5.65262, "None"),
				                                  new Vector3(6564.013, 773.3265, 6.590591, "None"),
				                                  new Vector3(6551.588, 777.1901, 6.788247, "None"),
				                                  
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Darnassus Portal Down and Auberdine",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });
			
			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(8753.117, 948.64, 25.66852, "None"),
				                                  new Vector3(8765.486, 956.6833, 29.10012, "None"),
				                                  new Vector3(8772.359, 960.1454, 30.11599, "None"),
				                                  new Vector3(8785.79, 966.983, 30.1999, "None")  {Action = moveforwardAction},
				                                  new Vector3(9946.25, 2612.97, 1316.245, "None"),
				                                  new Vector3(9946.107, 2594.624, 1316.194, "None"),
				                                  new Vector3(9946.558, 2576.157, 1318.328, "None"),
				                                  new Vector3(9946.901, 2556.688, 1316.229, "None"),
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Darnassus Portal Up",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });
			
			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(6572.557, 770.3295, 5.48198, "None"),
				                                  new Vector3(6580.037, 767.1205, 5.766551, "None") {Action = ruthEranAction},
				                                  new Vector3(8557.271, 1020.589, 5.375874, "None"),
				                                  new Vector3(8765.486, 956.6833, 29.10012, "None"),
				                                  new Vector3(8785.79, 966.983, 30.1999, "None") {Action = moveforwardAction},
				                                  new Vector3(9946.25, 2612.97, 1316.245, "None"),
				                                  new Vector3(9946.107, 2594.624, 1316.194, "None"),
				                                  new Vector3(9946.558, 2576.157, 1318.328, "None"),
				                                  new Vector3(9946.901, 2556.688, 1316.229, "None"),
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Auberdine to Rut'Theran and Darnassus Portal",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });
			
			
			PathFinder.OffMeshConnections.Add(new PathFinder.OffMeshConnection(new List<Vector3>
			                                  {
				                                  new Vector3(6572.557, 770.3295, 5.48198, "None"),
				                                  new Vector3(6580.037, 767.1205, 5.766551, "None") {Action = ruthEranAction},
				                                  new Vector3(8557.271, 1020.589, 5.375874, "None"),
			                                  }, (int) ContinentId.Kalimdor)
			                                  {
				                                  Type = PathFinder.OffMeshConnectionType.Unidirectional,
				                                  Name = "Auberdine to Rut'Theran",
				                                  TryToUseEvenIfCanFindPathSuccess = true
			                                  });
		}

		public static void ToAuberdine()
		{
			PluginLog.Log("Destination near Auberdine, take boat");
			_ship.TravelToDestination();
		}

		public static void ToRuthEran()
		{
			PluginLog.Log("Destination near Darnassus, take boat");
			_ship.TravelToDestination(true);
		}
	}
}

For the sake of at least protecting some of my "secrets" of several thousands of hours that went into HMP since 2016, I'm not going to share my ship implementations. They are simple enough that I think you could figure them out though.

Back when I started, none of this was documented and I basically had to trial and error through a lot of it or build things from scratch. Thankfully @Droidz was very, very helpful and even added more Offmesh functionality for people to use more easily. 

 

I hope this helps at least a few people in the future if they ever stumble upon this topic covering custom pathing, offmesh connections or Darnassus portals or ships. Of course, this can be adapted to add new elevators or similar portals too.

Link to comment
Share on other sites

Thanks for that-  it all goes a bit beyond my understanding at the moment as I'm probably where you were back in 2016 experimenting with stuff through trial and error!

Maybe you could help with another problem I'm having. I'm trying to make a plugin that automatically equips better gear, it works fine for the most part, except for some reason I can't understand it just can't pull the stats for certain items and it's driving me crazy. Basically, I create a tooltip from an itemlink then iterate over the lines, the problem I'm having is that sometimes the tooltip just seems to fail to get created.

Lua.LuaDoString("if not ScanTooltip then ScanTooltip = CreateFrame('GameTooltip', 'ScanTooltip', nil, 'GameTooltipTemplate'); end");
Lua.LuaDoString("ScanTooltip:SetOwner(UIParent, 'ANCHOR_NONE');");
Lua.LuaDoString("ScanTooltip:ClearLines();");
Lua.LuaDoString("ScanTooltip:SetHyperlink('" +itemLink+"');");
string[] tooltipLines = Lua.LuaDoString<string[]>("local StatsTable = {} for i = 1, ScanTooltip:NumLines() do local text=_G[\"ScanTooltipTextLeft\"..i]:GetText() table.insert(StatsTable, text) end return unpack(StatsTable);");

// DEBUG STUFF
Logging.Write("Item Link: " + itemLink);
Logging.Write("Number of Lines:" + Lua.LuaDoString<int>("return ScanTooltip:NumLines()"));
Lua.RunMacroText("/script print(\"Item: " + itemLink + "\")");

 The top part of the code segment is how I create and then scan the tooltips, below is the result of the 3 debug lines in the WRobot log and WoW chat window;

19:32:21 - [InventoryManager] Started.

19:32:23 - Item Link: |cff1eff00|Hitem:15512:0:0:0:0:0:1097:0|h[Grunt's Shield of the Boar]|h|r
19:32:23 - Number of Lines:0
19:32:23 - [InventoryManager] item "Grunt's Shield" id 15512 reports no stats - blacklisted

19:32:23 - Item Link: |cff1eff00|Hitem:14569:0:0:0:0:0:0:0|h[Bristlebark Bindings]|h|r
19:32:23 - Number of Lines:7
19:32:25 - [InventoryManager] equipping "Bristlebark Bindings" id 14569 value 480

19:32:26 - Item Link: |cff1eff00|Hitem:4697:0:0:0:0:0:0:0|h[Burnished Girdle]|h|r
19:32:26 - Number of Lines:7
19:32:28 - [InventoryManager] [InventoryManager] cannot equip item "Burnished Girdle" id 4697 - blacklisted

In example above the tooltip seems to fail to create for Grunt's shield, but in game the itemlink appears to be fine;

image.png.cacd50f384bdf3987aa2ea8b5e06e830.png

 

Any ideas what I could be doing wrong?

EDIT: I should point out that the above example makes it look like an issue specifically with items that have a suffix, however, it does work for some items with suffixes and it doesn't work for some items without them. I can't help but feel like I'm missing something really simple.

Link to comment
Share on other sites

I figured it out... I'm an idiot...

Lua.LuaDoString("ScanTooltip:SetHyperlink('" +itemLink+"');");

Changed to this;

Lua.LuaDoString("ScanTooltip:SetHyperlink(\"" +itemLink+"\");");

The items that were failing all contained an apostrophe...

Link to comment
Share on other sites

You shouldn't recreate tooltips. You should clean them up and re-use. They don't get garbage collected properly. I made a comment regarding that before. I don't remember the specifics, but that's basically what my research had shown

Link to comment
Share on other sites

4 hours ago, Matenia said:

You shouldn't recreate tooltips. You should clean them up and re-use. They don't get garbage collected properly. I made a comment regarding that before. I don't remember the specifics, but that's basically what my research had shown

Noted, thanks for tip - guessing just doing this covers it?

Lua.LuaDoString("if not ScanTooltip then ScanTooltip = CreateFrame('GameTooltip', 'ScanTooltip', nil, 'GameTooltipTemplate'); end");
Lua.LuaDoString("ScanTooltip:SetOwner(UIParent, 'ANCHOR_NONE');");
Lua.LuaDoString("ScanTooltip:ClearLines();");
Lua.LuaDoString("ScanTooltip:SetHyperlink(\"" +itemLink+"\");");

I don't set it to nil and only create the frame if it doesn't already exist.

Link to comment
Share on other sites

Yes, that should be correct. Some people have gone as far as clearing all the lines separately by iterating them. I think this was needed in Vanilla but not TBC.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...