What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

Bestiary Implementation

Infinite Monkeys

Community Developer
Joined
Jul 7, 2012
Messages
224
Reaction score
16
I'm trying to write an addon to capture beasts while mapping. It's currently finding the Bestiary monsters successfully, and approaching them, but the actual net input does nothing. This is what I have for the 'throw net' action:

Code:
LokiPoe.Input.HighlightObject(target);
if(LokiPoe.InGameState.CurrentTarget == target)
{
    GlobalLog.Warn($"[CaptureBeastTask] Throwing net at Bestiary monster: " + target.Name);
    LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.throw_net, true, false, false);
    return true;
}

The check succeeds and the 'throwing net' log message is displayed, but no net is actually thrown. I've also tried simulating a key down then a key up, like this:
Code:
if(LokiPoe.InGameState.CurrentTarget == target)
{
    GlobalLog.Warn($"[CaptureBeastTask] Throwing net at Bestiary monster: " + target.Name);
    LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.throw_net, true, false, false);
    await Wait.Sleep(20);
    LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.throw_net, false, false, true);
    return true;
}

That didn't work either. Anyone know how to get this working?

e: Also, when using the Task system, does returning true prevent the next tasks from being executed? It seems like the combat routine is running even when the Bestiary task returns true.
 
Out of curiosity, what do you see in the game client when this happens? Do you see any error about the target needing to be a beast? I wonder if your target is actually a beast. How are you getting "target"? I'm still trying to wrap my head around a way to do this same task.
 
In the next update I'll add this to Coroutines, maybe with some changes, not sure yet:
Code:
public static async Task<bool> ThrowNetAt(Monster obj)
        {
            if (obj == null)
            {
                Log.ErrorFormat("[ThrowNetAt] The object is null.");
                return false;
            }

            var id = obj.Id;

            await FinishCurrentAction();

            Log.DebugFormat("[ThrowNetAt] Now attempting to highlight {0}.", id);

            if (!LokiPoe.Input.HighlightObject(obj))
            {
                Log.ErrorFormat("[ThrowNetAt] The target could not be highlighted.");
                return false;
            }

            var target = LokiPoe.InGameState.CurrentTarget;
            if (target != obj)
            {
                Log.ErrorFormat("[ThrowNetAt] The target highlight has been lost.");
                return false;
            }

            Log.DebugFormat("[ThrowNetAt] Now attempting to throw the net at {0}.", id);

            LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.throw_net, true, false, false);

            await LatencyWait();

            await FinishCurrentAction(false);

            LokiPoe.ProcessHookManager.ClearAllKeyStates();

            return true;
        }

It's pretty much what InteractWith looks like, aside from not having to care about holding the control key.

If you use that code in DevTab, here's a snippet you can use on a small crab in Coast to test:
Code:
using System;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
using Buddy.Coroutines;
using Loki;
using Loki.Common;
using Loki.Game;
using Loki.Game.Objects;
using Loki.Game.GameData;
using Loki.Bot;
using Loki.Bot.Pathfinding;
using log4net;

public class RuntimeCode
{
    private static readonly ILog Log = Logger.GetLoggerInstanceForType();

    private Coroutine _coroutine;

    public void Execute()
    {
        using(LokiPoe.AcquireFrame())
        {
            LokiPoe.Input.Binding.Update();
            //return;

            ExilePather.Reload();
        }

        // Create the coroutine
        _coroutine = new Coroutine(() => MainCoroutine());

        // Run the coroutine while it's not finished.
        while(!_coroutine.IsFinished)
        {
            try
            {
                using(LokiPoe.AcquireFrame())
                {
                    _coroutine.Resume();
                }
            }
            catch (Exception ex)
            {
                Log.ErrorFormat("[Execute] {0}.", ex);
                break;
            }
        }

        // Cleanup the coroutine if it already exists
        if (_coroutine != null)
        {
            _coroutine.Dispose();
            _coroutine = null;
        }
    }

    private async Task<bool> UserCoroutine()
    {
        Log.InfoFormat("UserCoroutine ");

        var e = LokiPoe.ObjectManager.GetEnumFromMetadata("Metadata/Monsters/CrabParasite/CrabParasiteSmall");
        Log.Info(e);
       
        var mobs = LokiPoe.ObjectManager.GetObjects(e).Select(m => m as Monster).Where(m => m != null && m.IsActive).OrderBy(m => m.Distance).ToList();
        Log.InfoFormat("Found {0} mobs", mobs.Count);
       
        var mob = mobs.FirstOrDefault();
        if(mob != null)
        {
            Log.InfoFormat("[{0}] {1} [{2}% HP] ({3})", mob.Id, mob.Metadata, mob.HealthPercentTotal, mob.Distance);
            await Coroutines.ThrowNetAt(mob);
        }

        return false;
    }

    private async Task MainCoroutine()
    {
        while (true)
        {
            if (LokiPoe.IsInLoginScreen)
            {
                // Offload auto login logic to a plugin.
                foreach (var plugin in PluginManager.EnabledPlugins)
                {
                    await plugin.Logic(new Logic("login_screen"));
                }
            }
            else if (LokiPoe.IsInCharacterSelectionScreen)
            {
                // Offload character selection logic to a plugin.
                foreach (var plugin in PluginManager.EnabledPlugins)
                {
                    await plugin.Logic(new Logic("character_selection"));
                }
            }
            else if (LokiPoe.IsInGame)
            {
                // Execute user logic until false is returned.
                if(!await UserCoroutine())
                    break;
            }
            else
            {
                // Most likely in a loading screen, which will cause us to block on the executor,
                // but just in case we hit something else that would cause us to execute...
                await Coroutine.Sleep(1000);
                continue;
            }

            // End of the tick.
            await Coroutine.Yield();
        }
    }
}

Output would be something like:
Code:
Savage_Crab
Found 1 mobs
[812] Metadata/Monsters/CrabParasite/CrabParasiteSmall [42.85714% HP] (11)
[ThrowNetAt] Now attempting to highlight 812.
[ThrowNetAt] Now attempting to throw the net at 812.
[LatencyWait] Now sleeping 27 ms.
[FinishCurrentAction][False] Waited 2218 ms for the action to finish Capture Monster.

Key thing when running from DevTab is making sure you have updated input bindings first, otherwise the keys won't be known and input fails. You will also need to use the hotkey to enable input hooking (alt + shift + e) then i use the hotkey to run the devtab code (alt + shift + q) when client has focus to test code.

I just tested that code and it's working, so you should be able to start with that at minimal.
 
I ended up implementing necromancy nets instead, because it's much safer than trying to throw a net mid-fight. Obviously it doesn't work for certain builds though. My plugin gets stuck occasionally, and doesn't have any settings, so it's not worth releasing. Once that's added to Coroutines it'll probably be easier to get it working though.
 
You can check successful net throw via IsActive property, it will become false when you catch it. The problem is to trow net right in time, before you oneshot it or make it enrage.
 
I'm interested in this for sure is it something where you have to code in every mob you want to catch or can you set it to red/yellow skulls only?
 
Last edited:
There is a good ghetto solution for this. Download autokeyclicker. Set to 500ms or so to press your hotkey ( mine is V ). Set the bot to not stash nets. GGG made it so when you get to 50 pets in any cage the weakest one gets pushed out when you trap another one which makes this a viable way to deal with it until there's an add-on. It's not interfering with the bot at all and my cages are filling up, it gets the rares very easily because you are not one shotting them.
 
There is a good ghetto solution for this. Download autokeyclicker. Set to 500ms or so to press your hotkey ( mine is V ). Set the bot to not stash nets. GGG made it so when you get to 50 pets in any cage the weakest one gets pushed out when you trap another one which makes this a viable way to deal with it until there's an add-on. It's not interfering with the bot at all and my cages are filling up, it gets the rares very easily because you are not one shotting them.
Oh so what you are saying is it is just hitting the throw net button every half second at all times?
 
I really would not recommend spamming the net button like that, as you're going to be creating some weird actions on the server, and it will most likely be detectable as being "suspicious". The API already supported normal catching of monsters, so no reason to do that.

Tonight's update adds a lot more Bestiary data as well, so you can only net new stuff if you want, but you still don't have to worry about your menagerie filling up regardless for the previously mentioned reason - unless you want to craft lower level items for noobie chars, then you'll run into the game's design flaw of lower tier stuff being replaced. No way to avoid that really, but the basics of your capturable monsters data is now accessible. Please read the full patch notes to see the new APIs provided.
 
@pushedx Hey Pushedx that is great news! Does this mean that there are netting commands somewhere that I have missed or is it up to the community to make the plugins for netting?
 
May as well share my plugin for capturing dead beasts. It's not flawless, but it works ok. Make sure you have necro nets in your inventory (Cross-bot settings > Misc). It captures all two-mod beasts, but should be pretty easy to edit if you want something else.
 

Attachments

May as well share my plugin for capturing dead beasts. It's not flawless, but it works ok. Make sure you have necro nets in your inventory (Cross-bot settings > Misc). It captures all two-mod beasts, but should be pretty easy to edit if you want something else.
Badass man thank you!
 
Yeah you take off the option in AIF to stash all currency and it works!
 
Back
Top