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

Auto use of vendor recipe for maps (3x identical maps)

poe3210

New Member
Joined
Mar 25, 2014
Messages
17
Reaction score
0
For the purpose of writing a plugin to do the abovementioned process, is there API available to

1. look for map triplets in the stash, and for example, if x number of triplets are found,
2. withdraw all x sets of triplets
3. vendor them

algo would be something like this
1. read all maps in stash
2. count maps according to name of map, ->n
3. rounddown(n/3), ->x for each named map
4. if x >= 3, withdraw 3x of named map, else end
5. vendor. end.

very useful for cleaning up the stash since matching maps to vendor is quite intensive to do manually...
 
Last edited:
The actual API aspect would be the ability to see what's in stash, and then withdraw it. The answer is yes! The API supports that. As you can see with the regular bot, it can also go to a vendor, and sell items as well.

Recent updates have included logic that simplifies certain tasks so when you want to do something like this, it'll be much easier. However, all of that is not done, and there aren't any guides yet for it, since it's still being worked on and improved.

Here's an example bot that I just wrote and tested that does 1/2 of what you asked. You won't be able to compile and run it yet, since new stuff was added/updated to make it work correctly. However, it'll show you an example of how some of the stuff we're moving towards looks like.

WithdrawMapBot.cs
Code:
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using Buddy.Coroutines;
using log4net;
using Loki.Bot.v2;
using Loki.Game;
using Loki.Game.Inventory;
using Loki.TreeSharp;
using Loki.Utilities;
using Action = Loki.TreeSharp.Action;

namespace Loki.Bot.Logic.Bots.ApiTest
{
    /// <summary></summary>
    public class WithdrawMapBot
#if DEBUG
 : IBot
#endif
    {
        /// <summary></summary>
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        /// <summary>The main coroutine.</summary>
        private Coroutine _coroutine;

        /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. </summary>
        public void Dispose()
        {
        }

        /// <summary>Whether or not this bot requires game input to happen. </summary>
        public bool RequiresGameInput
        {
            get { return true; }
        }

        /// <summary> Gets the name of this bot. </summary>
        public string Name
        {
            get { return "WithdrawMapBot"; }
        }

        /// <summary> Gets the description of this bot. </summary>
        public string Description
        {
            get { return "Coroutine testing code."; }
        }

        /// <summary> Gets the configuration window for this bot. </summary>
        public Window ConfigWindow
        {
            get { return null; }
        }

        /// <summary> Gets the logic for this bot. </summary>
        public Composite Logic { get; private set; }

        /// <summary>Which items to pulse when this bot runs.</summary>
        public PulseFlags PulseFlags
        {
            get { return PulseFlags.All; }
        }

        /// <summary> Starts this bot. Do any initialization here. </summary>
        public void Start()
        {
            Logic = new Action(ret => RunStatus.Success);

            _coroutine = new Coroutine(Main());
        }

        /// <summary> Stops this bot. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            if (_coroutine != null)
            {
                _coroutine.Dispose();
                _coroutine = null;
            }

            _needsStashContents = true;
            _withdraw.Clear();
        }

        /// <summary> Pulses the bot. Do any update logic here. </summary>
        public void Pulse()
        {
            // Check to see if the coroutine is finished. If it is, stop the bot.
            if (_coroutine.Status == CoroutineStatus.RanToCompletion || _coroutine.Status == CoroutineStatus.Faulted)
            {
                var msg = string.Format("The bot coroutine has finished {0}",
                    _coroutine.Status == CoroutineStatus.RanToCompletion ? "successfully." : "due to an error.");

                Log.DebugFormat(msg);

                BotMain.Stop(msg);

                return;
            }

            // Otherwise, resume it.
            if (_coroutine.Status == CoroutineStatus.Runnable)
            {
                object value;
                while (_coroutine.Resume(out value) && value != LokiCoroutine.EndTick)
                {
                }
            }
        }

        private bool _needsStashContents = true;

        readonly Dictionary<string, int> _withdraw = new Dictionary<string, int>();

        private bool WithdrawItemsDelegate(InventoryItem item)
        {
            if (!item.Item.Type.Contains("/Maps/"))
                return false;

            var name = item.Name;

            if (!_withdraw.ContainsKey(name))
                return false;

            if (_withdraw[name] > 0)
            {
                _withdraw[name]--;
                return true;
            }

            return false;
        }

        private IEnumerator Main()
        {
            while (true)
            {
                bool res;

                yield return LokiCoroutine.EndTick;

                // Don't do anything if we are not in town.
                if (!LokiPoe.ObjectManager.Me.IsInTown)
                    continue;

                // If Stash is not open yet, go to it and open it.
                if (!LokiPoe.Gui.IsStashWindowOpen)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.OpenStashCoroutine();

                    res = (bool) Coroutine.Current.LastResult;
                    if (!res)
                    {
                        Log.ErrorFormat("[Main] OpenStashCoroutine failed. Stopping the bot.");
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    continue;
                }

                // If we haven't requested the stash contents, request them. If we don't actually need to, then the coroutine will finish
                // quickly, as it doesn't have to request the contents again.
                if (_needsStashContents)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.RequestStashContents();

                    res = (bool) Coroutine.Current.LastResult;
                    if (!res)
                    {
                        Log.ErrorFormat("[Main] RequestStashContents failed. Stopping the bot.");
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needsStashContents = false;
                }

                var maps = new Dictionary<string, int>();

                // At this point, we are now ready to do some logic.
                // Now just get all tabs, and access them.
                var tabs = Registry.GetAllStashPagesInDisplayOrder();
                foreach (var stashTab in tabs)
                {
                    var tab = LokiPoe.ObjectManager.Me.Inventory.GetStashTabNoRequest(stashTab.Item1);

                    if(tab == null)
                        continue;

                    foreach (var item in tab.Items)
                    {
                        if (!item.Item.Type.Contains("/Maps/"))
                            continue;

                        if (!maps.ContainsKey(item.Name))
                            maps[item.Name] = 0;

                        maps[item.Name]++;
                    }
                }

                // Loop through all the maps to figure out what we have to work with.
                foreach (var kvp in maps)
                {
                    if (kvp.Value >= 3)
                    {
                        Log.DebugFormat("We have {0}x {1}", kvp.Value, kvp.Key);
                        
                        // We want to withdraw 3, 6, 9, etc...
                        _withdraw.Add(kvp.Key, 3 * (kvp.Value / 3));
                    }
                }

                // Now withdraw items that match.
                yield return Coroutines.WithdrawItemsCoroutine(WithdrawItemsDelegate);
                res = (bool) Coroutine.Current.LastResult;
                if (!res)
                {
                    Log.ErrorFormat("[Main] WithdrawItemsCoroutine failed. Stopping the bot.");
                    BotMain.Stop("An unrecoverable error was encountered.");
                    break;
                }

                // At this point, we would go to the vendor and sell, but a nicely wrapped sell coroutine isn't
                // done yet. In addition, you'd want to have some extra checks to make sure you are getting your map
                // upgrade and not junk due to a recipe being broken.

                break;
            }
            // ReSharper disable once FunctionNeverReturns
        }
    }
}

As the bot evolves and we update what we had, doing stuff like this will be much easier than it currently is. In addition, we'll have a nice collection of guides and examples for users to get an idea of how to do things. You'll be able to run this example in the next Beta version.
 
hold on so does this plugin work or is it an example?
 
hold on so does this plugin work or is it an example?

That bot will work on the next Beta version which will be built today. All it will do is go to stash and withdraw sets of 3 items; you'll have to vendor them yourself. I'll post an updated example in this thread after it's done though as some of the coroutines will be updated today.
 
Here is a runnable version of a bot that will withdraw sets of 3 types of maps if they are normal/magic. To use, create a folder inside your Bots folder in the ExileBuddy main folder (create it if it doesn't exist) with the name of "WithdrawMapBot". Place the following file in the new folder so you have the path: ExilebuddyBETA 0.1.1862.786\Bots\WithdrawMapBot\WithdrawMapBot.cs.

WithdrawMapBot.cs
Code:
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using Buddy.Coroutines;
using log4net;
using Loki.Bot.v2;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Inventory;
using Loki.TreeSharp;
using Loki.Utilities;
using Action = Loki.TreeSharp.Action;

namespace Loki.Bot.Logic.Bots.ApiTest
{
    /// <summary></summary>
    public class WithdrawMapBot : IBot
    {
        /// <summary></summary>
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        /// <summary>The main coroutine.</summary>
        private Coroutine _coroutine;

        /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. </summary>
        public void Dispose()
        {
        }

        /// <summary>Whether or not this bot requires game input to happen. </summary>
        public bool RequiresGameInput
        {
            get { return true; }
        }

        /// <summary> Gets the name of this bot. </summary>
        public string Name
        {
            get { return "WithdrawMapBot"; }
        }

        /// <summary> Gets the description of this bot. </summary>
        public string Description
        {
            get { return "Coroutine testing code."; }
        }

        /// <summary> Gets the configuration window for this bot. </summary>
        public Window ConfigWindow
        {
            get { return null; }
        }

        /// <summary> Gets the logic for this bot. </summary>
        public Composite Logic { get; private set; }

        /// <summary>Which items to pulse when this bot runs.</summary>
        public PulseFlags PulseFlags
        {
            get { return PulseFlags.All; }
        }

        /// <summary> Starts this bot. Do any initialization here. </summary>
        public void Start()
        {
            Logic = new Action(ret => RunStatus.Success);

            _coroutine = new Coroutine(Main());
        }

        /// <summary> Stops this bot. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            if (_coroutine != null)
            {
                _coroutine.Dispose();
                _coroutine = null;
            }

            _needsStashContents = true;
            _needToClearInventory = true;
            _withdraw.Clear();
        }

        /// <summary> Pulses the bot. Do any update logic here. </summary>
        public void Pulse()
        {
            // Check to see if the coroutine is finished. If it is, stop the bot.
            if (_coroutine.Status == CoroutineStatus.RanToCompletion || _coroutine.Status == CoroutineStatus.Faulted)
            {
                var msg = string.Format("The bot coroutine has finished {0}",
                    _coroutine.Status == CoroutineStatus.RanToCompletion ? "successfully." : "due to an error.");

                Log.DebugFormat(msg);

                BotMain.Stop(msg);

                return;
            }

            // Otherwise, resume it.
            if (_coroutine.Status == CoroutineStatus.Runnable)
            {
                object value;
                while (_coroutine.Resume(out value) && value != LokiCoroutine.EndTick)
                {
                }
            }
        }

        private bool _needsStashContents = true;
        private bool _needToClearInventory = true;

        readonly Dictionary<string, int> _withdraw = new Dictionary<string, int>();

        private bool WithdrawItemsDelegate(InventoryItem item, object user)
        {
            if (!item.Item.Type.Contains("/Maps/"))
                return false;

            var name = item.Name;

            if (!_withdraw.ContainsKey(name))
                return false;

            // Don't use rare/unique maps.
            if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                return false;

            if (_withdraw[name] > 0)
            {
                // This is important so we don't mess up the state because an item cannot be fit!
                if (LokiPoe.ObjectManager.Me.Inventory.Main.CanFitItem(item.Item))
                {
                    _withdraw[name]--;
                }

                // We do want to withdraw the item, but since we know it can't fit, then the withdraw logic
                // will terminate early.
                return true;
            }

            return false;
        }

        private IEnumerator Main()
        {
            while (true)
            {
                bool res;

                yield return LokiCoroutine.EndTick;

                // Don't do anything if we are not in town.
                if (!LokiPoe.ObjectManager.Me.IsInTown)
                    continue;

                // If Stash is not open yet, go to it and open it.
                if (!LokiPoe.Gui.IsStashWindowOpen)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.OpenStashCoroutine();

                    res = (bool) Coroutine.Current.LastResult;
                    if (!res)
                    {
                        Log.ErrorFormat("[Main] OpenStashCoroutine failed. Stopping the bot.");
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    continue;
                }

                // If we haven't requested the stash contents, request them. If we don't actually need to, then the coroutine will finish
                // quickly, as it doesn't have to request the contents again.
                if (_needsStashContents)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.RequestStashContentsCoroutine();

                    var rsccerr = (Coroutines.RequestStashContentsCoroutineError)Coroutine.Current.LastResult;
                    if (rsccerr != Coroutines.RequestStashContentsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] RequestStashContentsCoroutine returned {0}. Stopping the bot.", rsccerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needsStashContents = false;
                }

                // It'd probably be a good idea if you stash everything in your inventory before trying to withdraw
                // a bunch of stuff to vendor!
                if (_needToClearInventory)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.StashItemsCoroutine(null);

                    var sicerr = (Coroutines.StashItemsCoroutineError)Coroutine.Current.LastResult;
                    if (sicerr != Coroutines.StashItemsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] StashItemsCoroutine returned {0}. Stopping the bot.", sicerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needToClearInventory = false;
                }

                var maps = new Dictionary<string, int>();

                // At this point, we are now ready to do some logic.
                // Now just get all tabs, and access them.
                var tabs = Registry.GetAllStashPagesInDisplayOrder();
                foreach (var stashTab in tabs)
                {
                    var tab = LokiPoe.ObjectManager.Me.Inventory.GetStashTabNoRequest(stashTab.Item1);

                    // If the tab is null, we don't have its contents.
                    if(tab == null)
                        continue;

                    foreach (var item in tab.Items)
                    {
                        if (!item.Item.Type.Contains("/Maps/"))
                            continue;

                        // Don't use rare/unique maps.
                        if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                            continue;

                        if (!maps.ContainsKey(item.Name))
                            maps[item.Name] = 0;

                        maps[item.Name]++;
                    }
                }

                // Loop through all the maps to figure out what we have to work with.
                foreach (var kvp in maps)
                {
                    if (kvp.Value >= 3)
                    {
                        Log.DebugFormat("We have {0}x {1}", kvp.Value, kvp.Key);
                        
                        // We want to withdraw 3, 6, 9, etc...
                        _withdraw.Add(kvp.Key, 3 * (kvp.Value / 3));
                    }
                }

                // Now withdraw items that match.
                yield return Coroutines.WithdrawItemsCoroutine(WithdrawItemsDelegate);
                var ret = (Coroutines.WithdrawItemsCoroutineError)Coroutine.Current.LastResult;
                if (ret == Coroutines.WithdrawItemsCoroutineError.OpenStashCoroutineFailed)
                {
                    Log.ErrorFormat("[Main] WithdrawItemsCoroutine returned {0}. Stopping the bot.", ret);
                    BotMain.Stop("An unrecoverable error was encountered.");
                    break;
                }
                
                if (ret == Coroutines.WithdrawItemsCoroutineError.CouldNotFitAllItems)
                {
                    Log.DebugFormat("[Main] There are more items to withdraw, but we are out of room.");
                }

                // At this point, we would go to the vendor and sell, but a nicely wrapped sell coroutine isn't
                // done yet. In addition, you'd want to have some extra checks to make sure you are getting your map
                // upgrade and not junk due to a recipe being broken.

                break;
            }
            // ReSharper disable once FunctionNeverReturns
        }
    }
}

That works with Beta #786. It'll search your stash for 3 types of the same map of magic/normal rarity and withdraw them until your inventory is full or no more are found. From there, you can do the vendoring yourself. You can make adjustments really to do other things besides maps, but that's just an example of how doing some stuff is looking. If you wanted to make a version that withdrew skill gems for you instead so you don't have to search stash, you certainly could.

The reason it's a bot and not a plugin is because the current GrindBot is not setup in a way to do things like this in a way that wouldn't interfere with what's there. For something like this, you want the bot to execute the logic until it finishes, and not be doing other things that would interrupt it.
 
Here is the new code for 787+ as some of the logic was updated.

WithdrawMapBot.cs
Code:
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using Buddy.Coroutines;
using log4net;
using Loki.Bot.v2;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Inventory;
using Loki.Game.NativeWrappers;
using Loki.TreeSharp;
using Loki.Utilities;
using Action = Loki.TreeSharp.Action;

namespace Loki.Bot.Logic.Bots.ApiTest
{
    /// <summary></summary>
    public class WithdrawMapBot
#if DEBUG
 : IBot
#endif
    {
        /// <summary></summary>
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        /// <summary>The main coroutine.</summary>
        private Coroutine _coroutine;

        /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. </summary>
        public void Dispose()
        {
        }

        /// <summary>Whether or not this bot requires game input to happen. </summary>
        public bool RequiresGameInput
        {
            get { return true; }
        }

        /// <summary> Gets the name of this bot. </summary>
        public string Name
        {
            get { return "WithdrawMapBot"; }
        }

        /// <summary> Gets the description of this bot. </summary>
        public string Description
        {
            get { return "Coroutine testing code."; }
        }

        /// <summary> Gets the configuration window for this bot. </summary>
        public Window ConfigWindow
        {
            get { return null; }
        }

        /// <summary> Gets the logic for this bot. </summary>
        public Composite Logic { get; private set; }

        /// <summary>Which items to pulse when this bot runs.</summary>
        public PulseFlags PulseFlags
        {
            get { return PulseFlags.All; }
        }

        /// <summary> Starts this bot. Do any initialization here. </summary>
        public void Start()
        {
            Logic = new Action(ret => RunStatus.Success);

            _coroutine = new Coroutine(Main());
        }

        /// <summary> Stops this bot. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            if (_coroutine != null)
            {
                _coroutine.Dispose();
                _coroutine = null;
            }

            _needsStashContents = true;
            _needToClearInventory = true;
            _withdraw.Clear();
        }

        /// <summary> Pulses the bot. Do any update logic here. </summary>
        public void Pulse()
        {
            // Check to see if the coroutine is finished. If it is, stop the bot.
            if (_coroutine.Status == CoroutineStatus.RanToCompletion || _coroutine.Status == CoroutineStatus.Faulted)
            {
                var msg = string.Format("The bot coroutine has finished {0}",
                    _coroutine.Status == CoroutineStatus.RanToCompletion ? "successfully." : "due to an error.");

                Log.DebugFormat(msg);

                BotMain.Stop(msg);

                return;
            }

            // Otherwise, resume it.
            if (_coroutine.Status == CoroutineStatus.Runnable)
            {
                object value;
                while (_coroutine.Resume(out value) && value != LokiCoroutine.EndTick)
                {
                }
            }
        }

        private bool _needsStashContents = true;
        private bool _needToClearInventory = true;

        readonly Dictionary<string, int> _withdraw = new Dictionary<string, int>();

        private bool WithdrawItemsDelegate(InventoryTab tab, InventoryItem item, object user)
        {
            if (!item.Item.Type.Contains("/Maps/"))
                return false;

            var name = item.Name;

            if (!_withdraw.ContainsKey(name))
                return false;

            // Don't use rare/unique maps.
            if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                return false;

            if (_withdraw[name] > 0)
            {
                // This is important so we don't mess up the state because an item cannot be fit!
                if (LokiPoe.ObjectManager.Me.Inventory.Main.CanFitItem(item.Item))
                {
                    _withdraw[name]--;
                }

                // We do want to withdraw the item, but since we know it can't fit, then the withdraw logic
                // will terminate early.
                return true;
            }

            return false;
        }

        private IEnumerator Main()
        {
            while (true)
            {
                bool res;

                yield return LokiCoroutine.EndTick;

                // Don't do anything if we are not in town.
                if (!LokiPoe.ObjectManager.Me.IsInTown)
                    continue;

                // If Stash is not open yet, go to it and open it.
                if (!LokiPoe.Gui.IsStashWindowOpen)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.OpenStashCoroutine();

                    res = (bool) Coroutine.Current.LastResult;
                    if (!res)
                    {
                        Log.ErrorFormat("[Main] OpenStashCoroutine failed. Stopping the bot.");
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    continue;
                }

                // If we haven't requested the stash contents, request them. If we don't actually need to, then the coroutine will finish
                // quickly, as it doesn't have to request the contents again.
                if (_needsStashContents)
                {
                    Log.DebugFormat("[Main] We need to request the contents of Stash.");

                    yield return Coroutines.RequestStashContentsCoroutine();

                    var rsccerr = (Coroutines.RequestStashContentsCoroutineError)Coroutine.Current.LastResult;
                    if (rsccerr != Coroutines.RequestStashContentsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] RequestStashContentsCoroutine returned {0}. Stopping the bot.", rsccerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needsStashContents = false;
                }

                // It'd probably be a good idea if you stash everything in your inventory before trying to withdraw
                // a bunch of stuff to vendor!
                if (_needToClearInventory)
                {
                    Log.DebugFormat("[Main] We need to stash the contents of our inventory.");

                    yield return Coroutines.StashItemsCoroutine();

                    var sicerr = (Coroutines.StashItemsCoroutineError)Coroutine.Current.LastResult;
                    if (sicerr != Coroutines.StashItemsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] StashItemsCoroutine returned {0}. Stopping the bot.", sicerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needToClearInventory = false;
                }

                var maps = new Dictionary<string, int>();

                // At this point, we are now ready to do some logic.
                // Now just get all tabs, and access them.
                var tabs = Registry.GetAllStashPagesInDisplayOrder();
                foreach (var stashTab in tabs)
                {
                    var tab = LokiPoe.ObjectManager.Me.Inventory.GetStashTabNoRequest(stashTab.Item1);

                    // If the tab is null, we don't have its contents.
                    if(tab == null)
                        continue;

                    foreach (var item in tab.Items)
                    {
                        if (!item.Item.Type.Contains("/Maps/"))
                            continue;

                        // Don't use rare/unique maps.
                        if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                            continue;

                        if (!maps.ContainsKey(item.Name))
                            maps[item.Name] = 0;

                        maps[item.Name]++;
                    }
                }

                // Loop through all the maps to figure out what we have to work with.
                foreach (var kvp in maps)
                {
                    if (kvp.Value >= 3)
                    {
                        Log.DebugFormat("We have {0}x {1}", kvp.Value, kvp.Key);
                        
                        // We want to withdraw 3, 6, 9, etc...
                        _withdraw.Add(kvp.Key, 3 * (kvp.Value / 3));
                    }
                }

                // Now withdraw items that match.
                yield return Coroutines.WithdrawItemsCoroutine(WithdrawItemsDelegate);
                var ret = (Coroutines.WithdrawItemsCoroutineError)Coroutine.Current.LastResult;
                if (ret == Coroutines.WithdrawItemsCoroutineError.OpenStashCoroutineFailed)
                {
                    Log.ErrorFormat("[Main] WithdrawItemsCoroutine returned {0}. Stopping the bot.", ret);
                    BotMain.Stop("An unrecoverable error was encountered.");
                    break;
                }
                
                if (ret == Coroutines.WithdrawItemsCoroutineError.CouldNotFitAllItems)
                {
                    Log.DebugFormat("[Main] There are more items to withdraw, but we are out of room.");
                }

                // At this point, we would go to the vendor and sell, but a nicely wrapped sell coroutine isn't
                // done yet. In addition, you'd want to have some extra checks to make sure you are getting your map
                // upgrade and not junk due to a recipe being broken.

                break;
            }
            // ReSharper disable once FunctionNeverReturns
        }
    }
}
 
this is great thanks!

EDIT: it works beautifully
 
Last edited:
Here is an update of the code to be compatible with the changes in Beta #796+
Code:
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using Buddy.Coroutines;
using log4net;
using Loki.Bot.v2;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Inventory;
using Loki.Game.NativeWrappers;
using Loki.TreeSharp;
using Loki.Utilities;
using Action = Loki.TreeSharp.Action;

namespace Loki.Bot.Logic.Bots.ApiTest
{
    /// <summary></summary>
    public class WithdrawMapBot
 : IBot
    {
        /// <summary></summary>
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        /// <summary>The main coroutine.</summary>
        private Coroutine _coroutine;

        /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. </summary>
        public void Dispose()
        {
        }

        /// <summary>Whether or not this bot requires game input to happen. </summary>
        public bool RequiresGameInput
        {
            get { return true; }
        }

        /// <summary> Gets the name of this bot. </summary>
        public string Name
        {
            get { return "WithdrawMapBot"; }
        }

        /// <summary> Gets the description of this bot. </summary>
        public string Description
        {
            get { return "Coroutine testing code."; }
        }

        /// <summary> Gets the configuration window for this bot. </summary>
        public Window ConfigWindow
        {
            get { return null; }
        }

        /// <summary> Gets the logic for this bot. </summary>
        public Composite Logic { get; private set; }

        /// <summary>Which items to pulse when this bot runs.</summary>
        public PulseFlags PulseFlags
        {
            get { return PulseFlags.All; }
        }

        /// <summary> Starts this bot. Do any initialization here. </summary>
        public void Start()
        {
            Logic = new Action(ret => RunStatus.Success);

            _coroutine = new Coroutine(Main());
        }

        /// <summary> Stops this bot. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            if (_coroutine != null)
            {
                _coroutine.Dispose();
                _coroutine = null;
            }

            _needsStashContents = true;
            _needToClearInventory = true;
            _withdraw.Clear();
        }

        /// <summary> Pulses the bot. Do any update logic here. </summary>
        public void Pulse()
        {
            // Check to see if the coroutine is finished. If it is, stop the bot.
            if (_coroutine.Status == CoroutineStatus.RanToCompletion || _coroutine.Status == CoroutineStatus.Faulted)
            {
                var msg = string.Format("The bot coroutine has finished {0}",
                    _coroutine.Status == CoroutineStatus.RanToCompletion ? "successfully." : "due to an error.");

                Log.DebugFormat(msg);

                BotMain.Stop(msg);

                return;
            }

            // Otherwise, resume it.
            if (_coroutine.Status == CoroutineStatus.Runnable)
            {
                object value;
                while (_coroutine.Resume(out value) && value != LokiCoroutine.EndTick)
                {
                }
            }
        }

        private bool _needsStashContents = true;
        private bool _needToClearInventory = true;

        readonly Dictionary<string, int> _withdraw = new Dictionary<string, int>();

        private bool WithdrawItemsDelegate(StashInventory tab, InventoryItem item, object user)
        {
            if (!item.IsMap)
                return false;

            var name = item.Name;

            if (!_withdraw.ContainsKey(name))
                return false;

            // Don't use rare/unique maps.
            if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                return false;

            if (_withdraw[name] > 0)
            {
                // This is important so we don't mess up the state because an item cannot be fit!
                if (LokiPoe.PlayerInventory.Main.CanFitItem(item.Item))
                {
                    _withdraw[name]--;
                }

                // We do want to withdraw the item, but since we know it can't fit, then the withdraw logic
                // will terminate early.
                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tab"></param>
        /// <param name="item"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        private int NumberOfStackedItemsToWithdraw(StashInventory tab, InventoryItem item, object user)
        {
            return item.Currency.StackCount;
        }

        private IEnumerator Main()
        {
            while (true)
            {
                bool res;

                yield return LokiCoroutine.EndTick;

                // Don't do anything if we are not in town.
                if (!LokiPoe.ObjectManager.Me.IsInTown)
                    continue;

                // If Stash is not open yet, go to it and open it.
                if (!LokiPoe.Gui.IsStashWindowOpen)
                {
                    Log.DebugFormat("[Main] The Stash window is not open. Now moving to Stash to open it.");

                    yield return Coroutines.OpenStashCoroutine();

                    res = (bool) Coroutine.Current.LastResult;
                    if (!res)
                    {
                        Log.ErrorFormat("[Main] OpenStashCoroutine failed. Stopping the bot.");
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    continue;
                }

                // If we haven't requested the stash contents, request them. If we don't actually need to, then the coroutine will finish
                // quickly, as it doesn't have to request the contents again.
                if (_needsStashContents)
                {
                    Log.DebugFormat("[Main] We need to request the contents of Stash.");

                    yield return Coroutines.RequestStashContentsCoroutine();

                    var rsccerr = (Coroutines.RequestStashContentsCoroutineError)Coroutine.Current.LastResult;
                    if (rsccerr != Coroutines.RequestStashContentsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] RequestStashContentsCoroutine returned {0}. Stopping the bot.", rsccerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needsStashContents = false;
                }

                // It'd probably be a good idea if you stash everything in your inventory before trying to withdraw
                // a bunch of stuff to vendor!
                if (_needToClearInventory)
                {
                    Log.DebugFormat("[Main] We need to stash the contents of our inventory.");

                    yield return Coroutines.StashItemsCoroutine();

                    var sicerr = (Coroutines.StashItemsCoroutineError)Coroutine.Current.LastResult;
                    if (sicerr != Coroutines.StashItemsCoroutineError.None)
                    {
                        Log.ErrorFormat("[Main] StashItemsCoroutine returned {0}. Stopping the bot.", sicerr);
                        BotMain.Stop("An unrecoverable error was encountered.");
                        break;
                    }

                    _needToClearInventory = false;
                }

                var maps = new Dictionary<string, int>();

                // At this point, we are now ready to do some logic.
                // Now just get all tabs, and access them.
                foreach (var tab in LokiPoe.PlayerInventory.GetAllStashTabs())
                {
                    foreach (var item in tab.Items)
                    {
                        if (!item.IsMap)
                            continue;

                        // Don't use rare/unique maps.
                        if (item.Item.Rarity == Rarity.Rare || item.Item.Rarity == Rarity.Unique)
                            continue;

                        if (!maps.ContainsKey(item.Name))
                            maps[item.Name] = 0;

                        maps[item.Name]++;
                    }
                }

                // Loop through all the maps to figure out what we have to work with.
                foreach (var kvp in maps)
                {
                    if (kvp.Value >= 3)
                    {
                        Log.DebugFormat("We have {0}x {1}", kvp.Value, kvp.Key);
                        
                        // We want to withdraw 3, 6, 9, etc...
                        _withdraw.Add(kvp.Key, 3 * (kvp.Value / 3));
                    }
                }

                // Now withdraw items that match.
                yield return Coroutines.WithdrawItemsCoroutine(WithdrawItemsDelegate, NumberOfStackedItemsToWithdraw);
                var ret = (Coroutines.WithdrawItemsCoroutineError)Coroutine.Current.LastResult;
                if (ret == Coroutines.WithdrawItemsCoroutineError.OpenStashCoroutineFailed)
                {
                    Log.ErrorFormat("[Main] WithdrawItemsCoroutine returned {0}. Stopping the bot.", ret);
                    BotMain.Stop("An unrecoverable error was encountered.");
                    break;
                }
                
                if (ret == Coroutines.WithdrawItemsCoroutineError.CouldNotFitAllItems)
                {
                    Log.DebugFormat("[Main] There are more items to withdraw, but we are out of room.");
                }

                // At this point, we would go to the vendor and sell, but a nicely wrapped sell coroutine isn't
                // done yet. In addition, you'd want to have some extra checks to make sure you are getting your map
                // upgrade and not junk due to a recipe being broken.

                break;
            }
            // ReSharper disable once FunctionNeverReturns
        }
    }
}
 
Last edited:
How and where do I get the WithdrawMapBot?

I tried creating a new CS file and copy pasting the code posted here but I don't see the plugin in the UI of the bot? Help please :D ty
 
How and where do I get the WithdrawMapBot?

I tried creating a new CS file and copy pasting the code posted here but I don't see the plugin in the UI of the bot? Help please :D ty

I explained it here. It goes in its own folder in Bots, not Plugins.
 
I explained it here. It goes in its own folder in Bots, not Plugins.

Once I put it there, where should it appear? I don't see anything in the API >.< Sorry for the bother

Created a folder named WithdrawMapBot, put WithdrawMapBot CSfile in it with the coding
 
if you've setup it correctly (correct file extension)
it should be at here:
choose.webp

if it shows up, just pickit and press start.
 
if you've setup it correctly (correct file extension)
it should be at here:
View attachment 126813

if it shows up, just pickit and press start.

Oh I see, thank you :)

Though I get this error, so it's not appearing :

Compiler Error: c:\Users\Pakamon\Desktop\-\EB\Bots\WithdrawMapBot\WithdrawMapBot.cs(137,46) : error CS1061: 'Loki.Game.Objects.LocalPlayer' does not contain a definition for 'Inventory' and no extension method 'Inventory' accepting a first argument of type 'Loki.Game.Objects.LocalPlayer' could be found (are you missing a using directive or an assembly reference?)
Compiler Error: c:\Users\Pakamon\Desktop\-\EB\Bots\WithdrawMapBot\WithdrawMapBot.cs(222,37) : error CS0117: 'Loki.Bot.Logic.Registry' does not contain a definition for 'GetAllStashPagesInDisplayOrder'
Compiler Error: c:\Users\Pakamon\Desktop\-\EB\Bots\WithdrawMapBot\WithdrawMapBot.cs(260,30) : error CS1501: No overload for method 'WithdrawItemsCoroutine' takes 1 arguments
 
Back
Top