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
}
}
}