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

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Chance some items

hmm, is it possible to enable and disable Itemfilters with a plugin?
Because then you could do like:
If nuff (>10) ChanceOrbs then enable ItemFilter DoNotSell:ChanceworthyItems & Pickup:ChanceworthyItems
Otherwise the Stash would be full in quite little time.

if not, you could add a second custom 'loot items task' before the basicgrindbot one, that will only pick up specific white items if X number of chance orbs are in your stash etc


Free up my time to release more code, help me pay my bills, and be awesome Support Me
 
Last edited:
hmm, is it possible to enable and disable Itemfilters with a plugin?
Because then you could do like:
If nuff (>10) ChanceOrbs then enable ItemFilter DoNotSell:ChanceworthyItems & Pickup:ChanceworthyItems
Otherwise the Stash would be full in quite little time.

Yes, you can. Here's a plugin example of something that will loot 2 Royal Skeans, then disable the pickup filter for them. To test, you can change to any other item and you'll want to drop at least 3 outside of 50 units of each other.
Code:
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using log4net;
using Loki.Bot;
using Loki.Bot.Logic.Bots.BasicGrindBot;
using Loki.Bot.v3;
using Loki.Game;
using Loki.Utilities;

namespace ItemFilterExample
{
    public class ItemFilterExample : IPlugin
    {
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        private bool _enabled;

        // We want to loot 2 Royal Skeans before disabling the filter.
        private int _royalSkeanCount = 2;

        #region Implementation of IPlugin

        /// <summary> The name of the plugin. </summary>
        public string Name
        {
            get { return "ItemFilterExample"; }
        }

        /// <summary> The description of the plugin. </summary>
        public string Description
        {
            get { return "A plugin that shows some example logic for changing item filters at runtime."; }
        }

        /// <summary>The author of the plugin.</summary>
        public string Author
        {
            get { return "Bossland GmbH"; }
        }

        /// <summary>The version of the plugin.</summary>
        public Version Version
        {
            get { return new Version(0, 0, 1, 1); }
        }

        /// <summary>Initializes this plugin.</summary>
        public void Initialize()
        {
            Log.DebugFormat("[ItemFilterExample] Initialize");
        }

        /// <summary> The plugin start callback. Do any initialization here. </summary>
        public void Start()
        {
            Log.DebugFormat("[ItemFilterExample] Start");
            BasicGrindBot.OnLoot += BgbOnOnLoot;
        }

        /// <summary> The plugin tick callback. Do any update logic here. </summary>
        public void Tick()
        {
        }

        /// <summary> The plugin stop callback. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            Log.DebugFormat("[ItemFilterExample] Stop");
            BasicGrindBot.OnLoot -= BgbOnOnLoot;
        }

        public JsonSettings Settings
        {
            get { return null; }
        }

        /// <summary> The plugin's settings control. This will be added to the Exilebuddy Settings tab.</summary>
        public UserControl Control
        {
            get { return null; }
        }

        /// <summary>Is this plugin currently enabled?</summary>
        public bool IsEnabled
        {
            get { return _enabled; }
        }

        /// <summary> The plugin is being enabled.</summary>
        public void Enable()
        {
            Log.DebugFormat("[ItemFilterExample] Enable");
            _enabled = true;
        }

        /// <summary> The plugin is being disabled.</summary>
        public void Disable()
        {
            Log.DebugFormat("[ItemFilterExample] Disable");
            _enabled = false;
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary> </summary>
        public void Dispose()
        {
        }

        #endregion

        #region Override of Object

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name + ": " + Description;
        }

        #endregion

        private void BgbOnOnLoot(object sender, BasicGrindBot.OnLootEventArgs onLootEventArgs)
        {
            if (onLootEventArgs.Name == "Royal Skean" && _royalSkeanCount > 0)
            {
                _royalSkeanCount--;

                Log.InfoFormat("[ItemFilterExample] We are about to loot a Royal Skean. We now need {0} more.",
                    _royalSkeanCount);

                if (_royalSkeanCount <= 0)
                {
                    // Only do this logic if the current item evaluator is DefaultItemEvaluator.
                    var itemEvaluator = ItemEvaluator.GetItemEvaluator() as DefaultItemEvaluator;
                    if (itemEvaluator == null)
                    {
                        return;
                    }

                    // Loop though the looting filter categories.
                    foreach (var category in itemEvaluator.ItemEvaluator.Categories)
                    {
                        // Only care about Pickup filters.
                        if (category.Type != ItemEvaluator.EvaluationType.PickUp)
                            continue;

                        // Loop though all current filters.
                        foreach (var filter in category.Filters)
                        {
                            // Only match our own filters. Please don't go around changing the user's other filters.
                            if (filter.Description == "ItemFilterExample-RoyalSkean")
                            {
                                Log.InfoFormat("[ItemFilterExample] Now disabling the {0} filter.", filter.Description);

                                // Disable the filter so no more items are looted.
                                filter.Enabled = false;

                                // This is specific to BasicGrindBot. We want to remove any items pending for looting, to avoid looting more items than we need.
                                // BGB caches items as they appear and evaluates them for looting once, to avoid performance issues and constant item evaluation.
                                var toRemove = new List<AreaStateCache.Location>();
                                foreach (var location in AreaStateCache.Current.ItemLocations)
                                {
                                    // We want to avoid removing the location of the item we're about to loot, so this should roughly do it.
                                    // It's not pefect, but it shouldn't matter much really. If tons of the item you want to loot at the same time drop,
                                    // you'd just have extras.
                                    if (location.Name == "Royal Skean" &&
                                        LokiPoe.Me.Position.Distance(location.Position) > 50)
                                    {
                                        Log.InfoFormat(
                                            "[ItemFilterExample] We will remove the location {0} at {1} to avoid excess looting of the item.",
                                            location.Name, location.Position);
                                        toRemove.Add(location);
                                    }
                                }
                                foreach (var location in toRemove)
                                {
                                    AreaStateCache.Current.RemoveItemLocation(location);
                                }

                                // TODO: This is not 100% correct, since the item filter supports custom paths. However, that's not used by the bot, 
                                // and the current implementation doesn't save the path interanlly. This will be changed later.
                                itemEvaluator.ItemEvaluator.Save(ItemEvaluator.DefaultPath);
                            }
                        }
                    }
                }
            }
        }
    }
}

I made a simple Pickup filter with the description of "ItemFilterExample-RoyalSkean" that simply looted Normal items by name. That should be enough to work from, but take care if you're going to go around modifying other filters.
 
Ok, thanks a bunch pushedx :)

my Problem now is, I partially get the syntax, but the most important parts are still missing :l
So I'll have to wait for darkbluefirefly to hack it together as he said :D

(btw, if you only collect chance items with ~3L it won't fill your stash as fast and the chance droprate is nearly the same)
 
Yes, you can. Here's a plugin example of something that will loot 2 Royal Skeans, then disable the pickup filter for them. To test, you can change to any other item and you'll want to drop at least 3 outside of 50 units of each other.
Code:
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using log4net;
using Loki.Bot;
using Loki.Bot.Logic.Bots.BasicGrindBot;
using Loki.Bot.v3;
using Loki.Game;
using Loki.Utilities;

namespace ItemFilterExample
{
    public class ItemFilterExample : IPlugin
    {
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        private bool _enabled;

        // We want to loot 2 Royal Skeans before disabling the filter.
        private int _royalSkeanCount = 2;

        #region Implementation of IPlugin

        /// <summary> The name of the plugin. </summary>
        public string Name
        {
            get { return "ItemFilterExample"; }
        }

        /// <summary> The description of the plugin. </summary>
        public string Description
        {
            get { return "A plugin that shows some example logic for changing item filters at runtime."; }
        }

        /// <summary>The author of the plugin.</summary>
        public string Author
        {
            get { return "Bossland GmbH"; }
        }

        /// <summary>The version of the plugin.</summary>
        public Version Version
        {
            get { return new Version(0, 0, 1, 1); }
        }

        /// <summary>Initializes this plugin.</summary>
        public void Initialize()
        {
            Log.DebugFormat("[ItemFilterExample] Initialize");
        }

        /// <summary> The plugin start callback. Do any initialization here. </summary>
        public void Start()
        {
            Log.DebugFormat("[ItemFilterExample] Start");
            BasicGrindBot.OnLoot += BgbOnOnLoot;
        }

        /// <summary> The plugin tick callback. Do any update logic here. </summary>
        public void Tick()
        {
        }

        /// <summary> The plugin stop callback. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            Log.DebugFormat("[ItemFilterExample] Stop");
            BasicGrindBot.OnLoot -= BgbOnOnLoot;
        }

        public JsonSettings Settings
        {
            get { return null; }
        }

        /// <summary> The plugin's settings control. This will be added to the Exilebuddy Settings tab.</summary>
        public UserControl Control
        {
            get { return null; }
        }

        /// <summary>Is this plugin currently enabled?</summary>
        public bool IsEnabled
        {
            get { return _enabled; }
        }

        /// <summary> The plugin is being enabled.</summary>
        public void Enable()
        {
            Log.DebugFormat("[ItemFilterExample] Enable");
            _enabled = true;
        }

        /// <summary> The plugin is being disabled.</summary>
        public void Disable()
        {
            Log.DebugFormat("[ItemFilterExample] Disable");
            _enabled = false;
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary> </summary>
        public void Dispose()
        {
        }

        #endregion

        #region Override of Object

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name + ": " + Description;
        }

        #endregion

        private void BgbOnOnLoot(object sender, BasicGrindBot.OnLootEventArgs onLootEventArgs)
        {
            if (onLootEventArgs.Name == "Royal Skean" && _royalSkeanCount > 0)
            {
                _royalSkeanCount--;

                Log.InfoFormat("[ItemFilterExample] We are about to loot a Royal Skean. We now need {0} more.",
                    _royalSkeanCount);

                if (_royalSkeanCount <= 0)
                {
                    // Only do this logic if the current item evaluator is DefaultItemEvaluator.
                    var itemEvaluator = ItemEvaluator.GetItemEvaluator() as DefaultItemEvaluator;
                    if (itemEvaluator == null)
                    {
                        return;
                    }

                    // Loop though the looting filter categories.
                    foreach (var category in itemEvaluator.ItemEvaluator.Categories)
                    {
                        // Only care about Pickup filters.
                        if (category.Type != ItemEvaluator.EvaluationType.PickUp)
                            continue;

                        // Loop though all current filters.
                        foreach (var filter in category.Filters)
                        {
                            // Only match our own filters. Please don't go around changing the user's other filters.
                            if (filter.Description == "ItemFilterExample-RoyalSkean")
                            {
                                Log.InfoFormat("[ItemFilterExample] Now disabling the {0} filter.", filter.Description);

                                // Disable the filter so no more items are looted.
                                filter.Enabled = false;

                                // This is specific to BasicGrindBot. We want to remove any items pending for looting, to avoid looting more items than we need.
                                // BGB caches items as they appear and evaluates them for looting once, to avoid performance issues and constant item evaluation.
                                var toRemove = new List<AreaStateCache.Location>();
                                foreach (var location in AreaStateCache.Current.ItemLocations)
                                {
                                    // We want to avoid removing the location of the item we're about to loot, so this should roughly do it.
                                    // It's not pefect, but it shouldn't matter much really. If tons of the item you want to loot at the same time drop,
                                    // you'd just have extras.
                                    if (location.Name == "Royal Skean" &&
                                        LokiPoe.Me.Position.Distance(location.Position) > 50)
                                    {
                                        Log.InfoFormat(
                                            "[ItemFilterExample] We will remove the location {0} at {1} to avoid excess looting of the item.",
                                            location.Name, location.Position);
                                        toRemove.Add(location);
                                    }
                                }
                                foreach (var location in toRemove)
                                {
                                    AreaStateCache.Current.RemoveItemLocation(location);
                                }

                                // TODO: This is not 100% correct, since the item filter supports custom paths. However, that's not used by the bot, 
                                // and the current implementation doesn't save the path interanlly. This will be changed later.
                                itemEvaluator.ItemEvaluator.Save(ItemEvaluator.DefaultPath);
                            }
                        }
                    }
                }
            }
        }
    }
}

I made a simple Pickup filter with the description of "ItemFilterExample-RoyalSkean" that simply looted Normal items by name. That should be enough to work from, but take care if you're going to go around modifying other filters.


Hmm with this partial code you "could" potentially shut off the item filter for the chaos recipe as well. Collecting one of each piece needed and shutting it off. Would you be able to re-enable it though
 
You mean like doing 1 Filter for each part of the Recipe and shutting each down. But ... you'd also have to do a ID Filter for each part, which you'd have to activate ...
 
You mean like doing 1 Filter for each part of the Recipe and shutting each down. But ... you'd also have to do a ID Filter for each part, which you'd have to activate ...

Well in theory after seeing that code yes you could. But honestly. You get 2chaos for unidentified rares so it wouldn't be necessary
 
Well, in the end you still have more, than you'd have when selling only unided or ided items
 
Correct! but who the hell is going to code this damn thing ahaha. I have tried but I am pressed for time to sit and I literally don't have a clue on coding. If there was a how to guide or something I'd read the shit out of it, most of the stuff I come up with on here I pull out of my ass or skim through what others have done and try to piece it all together
 
Hmm with this partial code you "could" potentially shut off the item filter for the chaos recipe as well. Collecting one of each piece needed and shutting it off. Would you be able to re-enable it though

There's no need to do that for the chance/chaos recipe. I already have logic included (ChaosChanceRecipe) that shows how that can be done and someone else made one too.

You could though, if you really wanted to, but since there's tons of other filters that might conflict, you'd also have to disable them too. I don't think playing with filters would be the solution for that task, but is reasonable for the task of limiting how much you pickup.

You can change the Enabled property to enable/disable the filter. So to turn it on rather than off, just set to true.

As for the process of randomly chancing items, then dropping them if they aren't what you want, that's totally doable, but there's some implementation issues with the current design that get in the way. Basically, I need to make WithdrawTask user configurable so other items can be withdrawn besides ID/TP. Next, Stashing logic needs to be user configurable in the same way, so items users want to withdraw, won't be put back into stash. Adding a task that simply drops items in your inventory that don't match any filters is easy as well, but also needs to be easily toggled on and off in the event the user wants to pickup an item and have it stashed while the bot is running, as opposed to having to create a filter for that item just to avoid the bot from dropping it the next time it starts.

This can currently be done by replacing tasks and adding some more logic yourself, but it's a bit of work.

I do have plans to change those aspects of the default tasks, but it's a longer term change right now.
 
@ pushedx - You'd actually don't even have to with draw the chances. I only pickup like 5 Sort of Items and only those with 3L or more so my stash doesn't get filled up too quickly.
Now the thing actually missing is the "GoToYouStash.ChanceItemX1->ChanceItemX2->ChanceItemX3."-task and so on. Because we could ... borrow / partially ( :P ) the Stashsearch stuff from exVaults MapRunner.
 
@ pushedx - You'd actually don't even have to with draw the chances. I only pickup like 5 Sort of Items and only those with 3L or more so my stash doesn't get filled up too quickly.
Now the thing actually missing is the "GoToYouStash.ChanceItemX1->ChanceItemX2->ChanceItemX3."-task and so on. Because we could ... borrow / partially ( :P ) the Stashsearch stuff from exVaults MapRunner.

You should post your plugin then :P lol
 
I'm an idea guy but not able to code myself :x
Doin the same shit for work :D
Well from trying it last night. The approach I would do is.
Since Push has the option in there to keep X item in inv for rolling strongboxes(plugin in the works btw =), just have to make sure I can kill all the mobs and return to location), keep The chances in inventory.
Roll the item as you get them,
If it's unique, go to town deposit, if it's rare, check it with current filter to not vendor or keep;
1. If mapping, check how much space left, vs portals left, vs current map completion. Make some assumed logic based on that to either keep the yellow for a town run or drop it.
2. If not mapping, keep it to sell when in town, or deposit.

That's how I would approach this, the chance of the item being kept is very, very low, therefore no need to run to town every time to get chance orbs to chance it. Do it while grinding/maping. That's how I would do it if I was manually playing.

Cheers.
 
Well from trying it last night. The approach I would do is.
Since Push has the option in there to keep X item in inv for rolling strongboxes(plugin in the works btw =), just have to make sure I can kill all the mobs and return to location), keep The chances in inventory.
Roll the item as you get them,
If it's unique, go to town deposit, if it's rare, check it with current filter to not vendor or keep;
1. If mapping, check how much space left, vs portals left, vs current map completion. Make some assumed logic based on that to either keep the yellow for a town run or drop it.
2. If not mapping, keep it to sell when in town, or deposit.

That's how I would approach this, the chance of the item being kept is very, very low, therefore no need to run to town every time to get chance orbs to chance it. Do it while grinding/maping. That's how I would do it if I was manually playing.

Cheers.

Yeah this is similar to how the competitors works I believe logic wise, I honestly would pay someone for this to work and for working chaos recipe. I mean working as in keeping one of each piece and selling when it has the piece during runs.
 
Besides, darkbluefly we really have no more community developers lol. I hope this changes soon as this is looking pretty grim for this bot
 
Besides, darkbluefly we really have no more community developers lol. I hope this changes soon as this is looking pretty grim for this bot
That's not true, ExVault, Naut, and some others dev. Some are better then me and do not publish, but keep it private, this is understandable.

Oh and yea I'll see if I can get it working.
 
That's not true, ExVault, Naut, and some others dev. Some are better then me and do not publish, but keep it private, this is understandable.

Oh and yea I'll see if I can get it working.

Totally understandable,

Um Naut is done with path for a while as he has a new job and won't have time for it, and Exvault sorry I forgot about my bad!
 
Besides, darkbluefly we really have no more community developers lol. I hope this changes soon as this is looking pretty grim for this bot

blasphemy and lies, this bot is all kinds of amazing ^_^


Due to popular demand, I put something basic together
no doubt darkblue's will be far more elegant when it's ready :)

IMPORTANT NOTE: this is barely tested, it was made quickly over a remote connection while at work. shh ;)


How to use:

place the file in ExileBuddy\Plugins\ChanceItems\
start ExileBuddy
click on the 'Settings' tab at the top
click on the 'BasicGrindBot' tab at the top
tick the box for 'FillChanceStacks' if it is not already ticked
click on the 'Plugins' tab at the top
click on 'ChanceItems' in the list to the left
tick the box for 'Enable Plugin', if it is not already ticked

set your loot filter to pick up the white items you want to chance:
click on the 'tools' tab at the top
click on 'Item Filter Editor'
follow the guide from https://www.thebuddyforum.com/exile...y-guides/157401-item-filter-editor-guide.html


when in town it will chance specified white items, assuming you have chance orbs in your inventory already. It will not get chance orbs from your stash, or use them directly from your stash
What it does after chancing will depend on your loot filters (if set to vendor magic / rare items, stash rares with specific attributes etc)
You need to specify what items to chance by editing the code, but it's basic and outlined below:

1. open ChanceItems.cs in notepad or similar
2. find line 184. Alternatively, use the notepad search feature, and search for 'FullName'

4. Edit this code, explanation and examples below
//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") ||
(item.FullName == "Glorious Plate")
//ENTER STUFF TO CHANCE ABOVE THIS

in the included example, it will chance any normal gavel, siege axe or glorious plate
You can add more by copying the format, and including another line, just change the name to what you want to chance. Note: If it is not the bottom item, it should have '||' at the end of the line

example adding 'occultist's vestment':

//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") ||
(item.FullName == "Glorious Plate") || <-- we added || at the end of this line, which was not there previously. This is because it is no longer the last item
(item.FullName == "Occultist's Vestment") <-- this does not have ||, as it is the last item
//ENTER STUFF TO CHANCE ABOVE THIS

Example removing 'Glorious Plate':

//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") <-- we removed the || as 'siege axe' is now the last item
//ENTER STUFF TO CHANCE ABOVE THIS


example of only 'siege axe':
//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Siege Axe") <-- we removed the || as 'siege axe' is the last (and only) item
//ENTER STUFF TO CHANCE ABOVE THIS

5. save the file in notepad
6. start exilebuddy, and hope there's no red text that it failed to compile :)


Do any developers have suggestions on how I can make this more user friendly?
I could have it chance any normal item without checking the name, but does anything else show up as rarity.Normal, such as quest items? which may cause problems
I could have textboxes in the plugin settings from within the bot, to type the names to check against. But I wouldn't know how to make a variable number of them, and it's unknown how many item types the user may want to chance. Could maybe get around it by making a large amount like 20


Thanks!

View attachment ChanceItems.cs


Free up my time to release more code, help me pay my bills, and be awesome Support Me
 
Last edited:
blasphemy and lies, this bot is all kinds of amazing ^_^


Due to popular demand, I put something basic together
no doubt darkblue's will be far more elegant when it's ready :)

IMPORTANT NOTE: this is barely tested, it was made quickly over a remote connection while at work. shh ;)


How to use:

place the file in ExileBuddy\Plugins\ChanceItems\
set your basicgrindbot settings to fill chance orbs
set your loot filter to pick up the white items you want to chance
when in town it will chance specified white items, assuming you have chance orbs in your inventory already. It will not get chance orbs from your stash, or use them directly from your stash
What it does after chancing will depend on your loot filters (if set to vendor magic / rare items, stash rares with specific attributes etc)
You need to specify what items to chance by editing the code, but it's basic and outlined below:

1. open ChanceItems.cs in notepad or similar
2. find line 184. Alternatively, use the notepad search feature, and search for 'FullName'

4. Edit this code, explanation and examples below
//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") ||
(item.FullName == "Glorious Plate")
//ENTER STUFF TO CHANCE ABOVE THIS

in the included example, it will chance any normal gavel, siege axe or glorious plate
You can add more by copying the format, and including another line, just change the name to what you want to chance. Note: If it is not the bottom item, it should have '||' at the end of the line

example adding 'occultist's vestment':

//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") ||
(item.FullName == "Glorious Plate") || <-- we added || at the end of this line, which was not there previously. This is because it is no longer the last item
(item.FullName == "Occultist's Vestment") <-- this does not have ||, as it is the last item
//ENTER STUFF TO CHANCE ABOVE THIS

Example removing 'Glorious Plate':

//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Gavel") ||
(item.FullName == "Siege Axe") <-- we removed the || as 'siege axe' is now the last item
//ENTER STUFF TO CHANCE ABOVE THIS


example of only 'siege axe':
//ENTER STUFF TO CHANCE BELOW THIS
(item.FullName == "Siege Axe") <-- we removed the || as 'siege axe' is the last (and only) item
//ENTER STUFF TO CHANCE ABOVE THIS

5. save the file in notepad
6. start exilebuddy, and hope there's no red text that it failed to compile :)


Do any developers have suggestions on how I can make this more user friendly?
I could have it chance any normal item without checking the name, but does anything else show up as rarity.Normal, such as quest items? which may cause problems
I could have textboxes in the plugin settings from within the bot, to type the names to check against. But I wouldn't know how to make a variable number of them, and it's unknown how many item types the user may want to chance. Could maybe get around it by making a large amount like 20


Thanks!

View attachment 156931

Bravo! seem's basic enough
 
Back
Top