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

HB PvP is absolutely terrible for some classes

Joker76

Member
Joined
Feb 8, 2010
Messages
835
Reaction score
9
Let me start off by saying that this is not a bash thread. I have been using HB and GB for a long time now and they have been very good to me. I have been able to level several 80s and farm some extra gold for things I might need to spend it on. The folks who donate their time to code free stuff for this community are awesome, as are the paid devs.

I will be providing some details here, in hopes that the devs will be able to fix some of these issues. I am not a "noob" to WoW or HB, I've been doing this for a while now. I'm no coding expert, but I even managed to write a couple small crafting plugins along the way. So ... if you want to be a wise ass and you don't have anything constructive to say, just move along and go chat in trade.

In the past I have used HB to PvP with Paladin, DK, Warrior, and Shaman. All of which worked very well, mostly because they were able to take a few hits along the way and deal some decent damage. Recently I have been leveling a Priest and a Rogue, two classes I have not played in the past. Both have been performing flawlessly using PvE grinding profiles and Instancebuddy. I decided this weekend to do some PvPing and that's when I started to run into major issues.

1. The Rogue. Level 52, Assassination spec. I figured with AB weekend I would be able to get at least 10 or so levels. After watching it for the first couple matches I can say with absolute certainty that if I let it run any longer I would have been reported. The thing is a suicide bot and nothing more. It runs to the SAME EXACT spots each time and get's annihilated. Anyone in their right mind would see this and realize it's a bot. I even saw people in chat saying "that Rogue must be a bot, he keeps charging Farm and getting killed". If you check the attached log, you will see that it died 44 times in about 30 minutes.

I tried the default CC, Shadowstrike and even some modified versions that are posted. All of them gave the same results. He would mount up at the beginning of the round, charge right into one of the bases (usually occupied by Horde) and just die. Most times he would not even target anything. Even if he did manage to target something (which was very rarely) he would stealth, sprint and maybe get off a couple abilities before getting destroyed. Now I realize some of this may be CC related but he was waiting until he got into range of ranged classes before even thinking about going into stealth. This is a huge fail.

I even tried using the Squire plugin to follow other players. But that's all it would do ... just follow around some random player. It never stealthed, hardly ever targeted anyone or used abilities. And it just looked completely weird in general. Like I was just following some random dude around the BG. I can only imagine how annoyed they must have been, watching me follow them around and do nothing.

2. The Priest. Now level 80, Disc/Shadow. Running random BGs as Disc mostly for honor points to buy some PvP gear. Using the Isis CC by Raphus. When it actually finds a group of people to heal, it works rather well. However, it normally has the same problem as the Rogue. It runs into a group of Horde over and over and over again ... and just dies. Squire worked better with the Priest, but it still has some weird issues. Sometimes it seems like the CC was not taking over as it should while Squire was active (ie. it would follow someone and just stand around like the Rogue does). Other times it would kick in and heal as expected.

As Shadow, it seems to have the same issues as the Rogue. It charges into hotspots and usually gets killed unless there happens to be some friendly players nearby (which there never is). It performs OK when it's 1v1 against another player, but that rarely happens.

There are some obvious issues with CCs, meshing and overall PvP logic. I have recommended HB to friends in the past, but as of this writing I will no longer recommend it for anything other than simple PvE grinding.

In short, I'm pissed off and tired after dicking around with this stuff all damn day. I've tried different CCs, w00t's PvP profiles, different plugins and even different versions of HB. Nothing seems to help. The bot performs very poorly for me in PvP and the only thing it has accomplished is getting me one step closer to being reported.

Again, I'm not attacking anyone or trying to bash the product. I am simply very confused because it has worked so well in the past on other classes. I really appreciate all the volunteer work that people do for this community. The free CCs, profiles and plugins are amazing. I simply wish for the paid devs to step up and fix some of these issues. It's supposed to be a PvP bot for goodness sake!

/rant
 

Attachments

I ran my warrior briefly when I first got hb earlier this year in a bg, all it took was for one session to see the problem of trying to bot in a bg. So I dedicate my ab and av weekends to just doing it manually while I let other toons use the HB to grind/quest and I can keep an eye on them. I'm highly paranoid anyway so anything that looks bottish I just turn it off. It has helped a great deal in leveling toons, but just not so much in AB or wsg. AV is a bit different but even still I find it more effective to just to it manually.
I am working on my rogue right now and have done 10 levels in about 11 hours manually in ab.

Morga
 
It runs to the SAME EXACT spots each time and get's annihilated. Anyone in their right mind would see this and realize it's a bot. I even saw people in chat saying "that Rogue must be a bot, he keeps charging Farm and getting killed". If you check the attached log, you will see that it died 44 times in about 30 minutes.

You should try the Squire plugin, i dunno how it works for melee, but basically it follows someone, so you run into battle with them instead of wandering around going to hotspot to hotspot and dying. Atleast you fight = )
 
I even tried using the Squire plugin to follow other players. But that's all it would do ... just follow around some random player. It never stealthed, hardly ever targeted anyone or used abilities. And it just looked completely weird in general. Like I was just following some random dude around the BG. I can only imagine how annoyed they must have been, watching me follow them around and do nothing.

He did use it.
 
with no disrespect to the makers of honorbuddy, the default pvp bot is shithouse (sorry). The targeting logic is bad, the movement logic is bad (not that mine is much better, but using hotspot pathing in BG just retarded), as is the moment at which it gives control to the CC. I haven't used it in a while. I started playing a warrior, and instead of making a CC I just make a whole bot with behavior appropriate for the class. this involves rewriting all the queuing code, resurrection code etc, but IMO its worth it

here's what im using on my level 18~ warrior rofl (work in progress) - look at GetTarget and MoveToCluster. It is extremely responsive and doesn't act like a goddamn downs syndrome during fights (standing still while getting beat on etc)

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx;
using Styx.Bot;
using TreeSharp;
using Styx.Logic;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals;
using Styx.Logic.Pathing;
using Styx.Logic.Combat;
using Styx.Helpers;
using Styx.Logic.BehaviorTree;

using Action = TreeSharp.Action;
using Timer = System.Timers.Timer;
using System.Diagnostics;

namespace Styx.Bot.Plugins
{
    class FuriousPvP : BotBase
    {

        private Timer despasticate;
        private Random rng;

        private BattlegroundType[] toQueue =
        {
            BattlegroundType.WSG,
            BattlegroundType.AB
        };

        private Timer rezBugWorkaround;

        public FuriousPvP()
        {
            despasticate = null;
            rng = new Random();
            rezBugWorkaround = null;
        }

        public override void Initialize()
        {
        }

        public override void Start()
        {
        }

        public override void Stop()
        {
        }

        public override System.Windows.Forms.Form ConfigurationForm
        {
            get
            {
                return null;
            }
        }
        public override string Name
        {
            get
            {
                return "PvP Fury Warrior";
            }
        }

        private Composite _root;

        public override Composite Root
        {
            get
            {
                if (_root == null)
                {
                    _root = new Action(ret => Dongs());
                }
                return _root;
            }
        }

        public override PulseFlags PulseFlags
        {
            get
            {
                return PulseFlags.All;
            }
        }

        public void Dongs()
        {
            ObjectManager.Update();
            if (Me == null || !Me.IsValid || !StyxWoW.IsInWorld || StyxWoW.GameState == GameState.Zoning)
            {
                return;
            }
            else
            {
                if (Battlegrounds.IsInsideBattleground)
                {
                    if (Battlegrounds.Finished)
                    {
                        Battlegrounds.LeaveBattlefield();
                    }
                    else if (Me.Dead)
                    {
                        ReleaseSpirit();
                    }
                    else if (Me.IsGhost)
                    {
                        Resurrect();
                    }
                    else if (Me.IsAlive)
                    {
                        rezBugWorkaround = null;

                        if (CC("Battle Shout") && !isAuraActive("Battle Shout", Me))
                        {
                            C("Battle Shout");
                        }
                        else
                        {
                            WoWPlayer target = GetTarget();
                            Logging.Write("gettarget: " + target);
                            if (target != null)
                            {
                                Fight(GetTarget());
                            }
                            else
                            {
                                MoveToCluster();
                            }
                        }
                    }
                }
                else
                {
                    if (IsAnyQueue(BattlegroundStatus.Confirm))
                    {
                        JoinBG();
                    }
                    else if (!isAuraActive("Deserter") && IsAnyQueue(BattlegroundStatus.None))
                    {
                        QueueUp();
                    }
                }
            }
        }

        private string DeUnicodify(string s)
        {

            StringBuilder sb = new StringBuilder();
            byte[] bytes = Encoding.UTF8.GetBytes(s);
            foreach (byte b in bytes)
            {
                if (b != 0)
                    sb.Append("\\" + b);
            }
            return sb.ToString();
        }

        private void Fight(WoWPlayer p)
        {
            if (p.Distance2D > 50)
            {
                MoveTo(p.Location, false);
                return;
            }
            if (!p.InLineOfSight)
            {
                MoveTo(p.Location, true);
                return;
            }
            if (p.Distance2D > 3)
            {
                MoveTo(p.Location, true);
            }
            if (Me.CurrentTargetGuid != p.Guid)
            {
                Lua.DoString("TargetUnit(\"" + DeUnicodify(p.Name) + "\")");
                return;
            }
            if (!Me.IsAutoAttacking)
            {
                Lua.DoString("StartAttack();");
            }
            if (!Me.IsFacing(p))
            {
                p.Face();
            }
            if (!Me.Combat && (p.Distance2D >= 8 && p.Distance2D <= 25) && CC("Charge", p))
            {
                C("Charge", p);
            }
            else if (p.Distance2D <= 10 && !p.ActiveAuras.ContainsKey("Piercing Howl") && CC("Piercing Howl"))
            {
                C("Piercing Howl");        
            }
            else if (!StyxWoW.GlobalCooldown && Me.Combat && p.Distance2D <= 5)
            {
                if (CC("Victory Rush", p))
                {
                    C("Victory Rush", p);
                }
                else if (CC("Execute", p))
                {
                    C("Execute", p);
                }
                else if (CC("Overpower", p))
                {
                    C("Overpower", p);
                }
                else if (CC("Bloodthirst", p))
                {
                    C("Bloodthirst", p);
                }
                else if (CC("Heroic Strike", p))
                {
                    C("Heroic Strike", p);
                }
                else if (isAuraActive("Rend", p) && CC("Rend", p))
                {
                    C("Rend", p);
                }
                else if (CC("Strike", p))
                {
                    C("Strike", p);
                }
            }
        }

        private IEnumerable<WoWPlayer> Frends()
        {
            List<WoWObject> list = WoWInternals.ObjectManager.ObjectList;
            List<WoWPlayer> ret = new List<WoWPlayer>();
            foreach (WoWObject obj in list)
            {
                if (obj is WoWPlayer)
                {
                    WoWPlayer p = (WoWPlayer)obj;
                    if (p.IsValid)
                    {
                        if (p.IsAlliance == Me.IsAlliance && !p.Dead && !p.IsGhost)
                        {
                            ret.Add(p);
                        }
                    }
                }
            }
            return ret;
        }


        private IEnumerable<WoWPlayer> Unfrends()
        {
            List<WoWObject> list = WoWInternals.ObjectManager.ObjectList;
            List<WoWPlayer> ret = new List<WoWPlayer>();
            foreach (WoWObject obj in list)
            {
                if (obj is WoWPlayer)
                {
                    WoWPlayer p = (WoWPlayer)obj;
                    if (p.IsValid)
                    {
                        if (p.IsAlliance != Me.IsAlliance && !p.Dead && !p.IsGhost)
                        {
                            ret.Add(p);
                        }
                    }
                }
            }
            return ret;
        }

        private void MoveToCluster()
        {
            var players = from player in Frends()
                          where !player.IsMe
                          select player;

            WoWPlayer best = null;
            int bestCount = 0;
            foreach (WoWPlayer p in players)
            {
                int count = 0;
                foreach (WoWPlayer compare in players)
                {
                    if (compare.Guid == p.Guid)
                    {
                        continue;
                    }
                    else if (p.Location.Distance2D(compare.Location) <= 60)
                    {
                        count++;
                    }
                }
                if (count > bestCount)
                {
                    bestCount = count;
                    best = p;
                }
            }
            if (best != null && best.Distance2D > 15)
            {
                Logging.Write("bestmove: " + best + "," + bestCount);
                MoveTo(best.Location, false);
            }
            else if (rng.Next(50) == 5)
            {
                Lua.DoString("JumpOrAscendStart()");
                Lua.DoString("AscendStop()");
            }
        }

        private bool IsAnyQueue(BattlegroundStatus status)
        {
            BattlegroundStatus first = Battlegrounds.GetQueuedBattlegroundInfo(0).Status;
            if (toQueue.Length == 1)
            {
                return first == status;
            }
            else
            {
                return first == status || Battlegrounds.GetQueuedBattlegroundInfo(1).Status == status;
            }
        }

        private void JoinBG()
        {
            Battlegrounds.AcceptBattlefieldPort(Battlegrounds.GetQueuedBattlegroundInfo(0).Status == BattlegroundStatus.Confirm
                ? 0 : 1, true);
        }

        private void QueueUp()
        {
            //Logging.write("Queuing for BG");
            foreach (BattlegroundType type in toQueue)
            {
                Battlegrounds.JoinBattlefield(type, Me.IsInParty);
            }
        }

        private void ReleaseSpirit()
        {
            //Logging.write("Releasing");
            Lua.DoString("RepopMe()");
        }

        private void Resurrect()
        {
            //Logging.write("Resurrecting");
            if (rezBugWorkaround == null)
            {
                rezBugWorkaround = new Timer(40 * 1000);
                rezBugWorkaround.AutoReset = false;
                rezBugWorkaround.Enabled = true;
            }
            else if (!rezBugWorkaround.Enabled)
            {
                //Logging.write("Resurrection bugged");
                // Bugged, need to talk to spirit rezzer            
                var spiritHealers = (from unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                                     orderby unit.Distance2D ascending
                                     where unit.IsSpiritHealer || unit.IsSpiritGuide
                                     select unit
                                    ).FirstOrDefault();

                if (spiritHealers != null)
                {
                    if (spiritHealers.Distance2D > 3)
                    {
                        Navigator.MoveTo(spiritHealers.Location);
                    }
                    else
                    {
                        spiritHealers.Interact();
                        rezBugWorkaround = null;
                    }
                }
            }
        }

        private WoWPlayer GetTarget()
        {
            return (from unit in Unfrends()
                    where unit.Distance2D < 50
                    orderby unit.Distance2D ascending
                    select unit).FirstOrDefault();

        }

        private void MoveTo(WoWPoint p, bool spastic)
        {
            if (!Me.IsMoving && p.Distance2D(Me.Location) > 40 && !Me.Mounted && Mount.CanMount())
            {
                Mount.MountUp();
            }
            else if (!spastic)
            {
                if (!Me.IsMoving)
                {
                    despasticate = new Timer();
                    despasticate.Interval = 5 * 1000;
                    despasticate.AutoReset = false;
                    despasticate.Enabled = true;
                    Navigator.MoveTo(p);
                }
                else
                {
                    if(despasticate == null || !despasticate.Enabled)
                    {
                        despasticate = new Timer();
                        despasticate.Interval = 5 * 1000;
                        despasticate.AutoReset = false;
                        despasticate.Enabled = true;
                        Navigator.MoveTo(p);
                    }
                }
            }
            else
            {
                despasticate = null;
                Navigator.MoveTo(p);
            }

        }

        private static LocalPlayer Me
        {
            get
            {
                return ObjectManager.Me;
            }
        }

        private bool isAuraActive(string name)
        {
            return isAuraActive(name, Me);
        }

        private bool isAuraActive(string name, WoWUnit u)
        {
            string s = Lua.GetReturnVal<string>("a,_,_,_,_,_,_,_,_,_,_ = UnitAura(\"" + DeUnicodify(u.Name) + 
                                                "\", \"" + DeUnicodify(name) + "\"); return a;", 0);
            return s != null;
        }

        private bool CC(string spell, WoWUnit target)
        {
            return SpellManager.CanCast(spell, target);
        }

        private bool CC(string spell)
        {
            return SpellManager.CanCast(spell);
        }

        private void ChainSpells(params string[] spells)
        {
            string macro = "";
            foreach (string s in spells)
            {
                macro += "CastSpellByName(\"" + s + "\", true);";
            }
            Lua.DoString(macro);
        }

        private bool C(string spell, WoWUnit target)
        {
            if (SpellManager.Cast(spell, target))
            {
                Logging.Write("cast " + spell + " " + target);
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool C(string spell)
        {
            Logging.Write("selfcast " + spell);
            return SpellManager.Cast(spell);
        }


    }
}

it might not be the greatest but I am getting better outcomes on this hacked-together thing than trying to adhere to the hb framework
 
Last edited:
W00t's pvp profiles are very good although I've only used them for wsg a few times. Also, while looking at a youtube video someone posted a while ago in these forums that was about how to spot botters in battlegrounds, I realized the first thing he mentioned was the bots that would follow someone and hardly ever attack. Squire imo is a bad idea unless you are a healer in which case, go for it.
 
i dont pvp bot for pretty much the exact reasons mentioned... i tried it a few times, and it just sucks. Amazing bot for grinding/questing and now even good at instances.. but pvp? nope.
 
Well, it seems I'm not alone here.

Would like to see a response from the devs, or at least a "yes, we're working on fixing some things" or "tough shit, deal with it".
 
Well, it seems I'm not alone here.

Would like to see a response from the devs, or at least a "yes, we're working on fixing some things" or "tough shit, deal with it".
I'd place a bet on the latter - it's not exactly high priority
 
I'd place a bet on the latter - it's not exactly high priority

Care to elaborate?

I would venture to guess that fixing obvious issues that are getting people reported would be a high priority. Especially when you're marketing a "PvP bot".
 
To me it seems like the principal use of HB/GB is grinding and harvesting, all of the other modes are experimental - despite what the marketing says.
 
I have to agree somewhat. Certain classes are horrible to bot with. Others are magic. I'm using ski's warlock cc and I must say my lock dominates in BGs. I've watched him for hours and he just performs tremendously. I'll chalk that up to the fact that Ski just makes good ccs. Now I wish there was a solid DK cc out there :)
 
Hey Joker76,
I can tell you my view and experience witch botting in PvP.
My First Character I bottet was my Tauren Warrior. He started running through AB weekend with level 61. At the end of the weekend he was 71.
No Report and enough Honorpoints to buy 3 264iLevel PvP Items. The next Weekend was Alterac Valley and I hit 80 after the second day.
At level 80 my warrior runs now almost every PvP weekend except of Eye of Storms. I've done that on every Character I've botted and there was no Report
and every Character now has a basic PvP gear. In that hole time I only got one report in Eye of Storms, yeah okay in doubt the map is to small and the amount of people in there is small
so it's highly dangerous to bot there. Use some improved and randomized PvP profiles and you will be happy again.
As for now I've started using Instancebuddy to gain my PvE stuff, looks pretty good everyday I can buy like 2 pieces of PvE gear.

Greets Karpfenmann
 
If anyone actually read the logs instead of assuming something, you would know that its having pathing issues.

Code:
[11:29:37 PM:252] Could not generate path from <2177.349, 1491.771, 1112.076> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:37 PM:567] Could not generate path from <2175.939, 1482.397, 1097.574> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:37 PM:903] Could not generate path from <2174.448, 1472.488, 1080.151> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:38 PM:136] Could not generate path from <2173.404, 1465.546, 1066.663> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:38 PM:385] Could not generate path from <2172.293, 1458.159, 1051.688> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:38 PM:635] Could not generate path from <2171.173, 1450.713, 1036.591> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:38 PM:935] Could not generate path from <2169.834, 1441.813, 1018.547> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:39 PM:193] Could not generate path from <2168.723, 1434.426, 1003.57> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:39 PM:435] Could not generate path from <2167.607, 1427.01, 988.5327> to <2186.026, 1570.535, 1159.358> (start)!
[11:29:39 PM:672] Cleared POI
[11:29:39 PM:672] I died.
Code:
[11:32:45 PM:117] [Squire] leader not moving, stopping...
[11:32:50 PM:202] Dismount to get unstuck.
[11:32:52 PM:414] Generated path from <2281.138, 1551.883, 1167.168> to <2287.994, 1596.387, 1165.489> in 156 ms (0.16 seconds)
[11:33:15 PM:248] Activity: Leaving battleground.
[11:33:15 PM:249] Honorbuddy was unable to find the profile: 
[11:33:16 PM:442] Loaded new tiled mesh for map "Northrend".
Code:
[11:37:18 PM:772] [Squire] leader is no longer in group
[11:37:18 PM:775] [Squire] >>> following Warlock.22894 with player density 5
[11:37:34 PM:694] Could not generate full path from {X:2517.027 Y:1597.725 Z:1266.412} to {X:2489.782 Y:1602.473 Z:1251.047} (distance from end point to destination: 4.294153 yards)!
[11:37:36 PM:696] Could not generate full path from {X:2505.412 Y:1593.855 Z:1261.38} to {X:2486.342 Y:1611.512 Z:1234.025} (distance from end point to destination: 6.319731 yards)!
[11:37:39 PM:841] Mounting: Swift Palomino
[11:37:42 PM:355] Stop and dismount...
[11:37:42 PM:405] Dismount to get unstuck.
[11:37:44 PM:930] [Squire] unable to mount after 272 ms
[11:38:02 PM:688] Mounting: Swift Palomino
[11:38:09 PM:930] [Squire] character was stopped but somehow moved 14.640 yds
[11:38:09 PM:938] [Squire] leader died
[11:38:09 PM:940] [Squire] >>> following Warrior.23101 with player density 3
[11:38:10 PM:494] [Squire] unable to mount after 260 ms
[11:38:13 PM:309] [Squire] An Exception occured. Check debug log for details.

Go shadow and see what happens w/o squire (Isis Shadow). Mine performs just fine, it runs the flag and everything and usually melts faces.

The devs are working on the pathing issues
 
Kickazz006;163327 [B said:
The devs are working on the pathing issues[/B]

How do you know they are working on pathing issues?
 
I too am scared to leave mine AFK BG's. It's a suicide Bot, looks bad, makes me look bad. Odd considering the app is called "Honor"buddy lol. It is very good at everything else though. What i would like to see is an option setting for each PvP in a GUI that will allow you to select soemthing like "Protect Bases" "Cap Flags Stealth" "Healbot Highest HK'er" stuff like that would add randomness to the bot and almost take away predictable behavior.
 
I agree, the Bot isn't good enough to run in battlegrounds, unattended or not. It's one thing to blindly bash monsters out in the world where no one's looking, it's an entirely different thing altogether to play against highly skilled players who want you dead. I only use it for leveling because of this. It IS ironic that the thing the program is named after, is the thing it does the worst at. :-D

Even for world playing, the bot suffers, and requires the use of 'black spots' to avoid problem areas. For instance, it's not smart enough to determine that there are 17 mosshide mongrels surrounding that peacebloom, and it should probably leave well enough alone; the bot will charge in anyway to get it, dying over and over and over again while you sleep, even though the armor and sword are at 0 durability. A smarter bot, would detect how many players are clustered near the proposed target, and slink away if there were too many.

In addition, a smarter bot would detect if the armor had been beaten off its body, and if so, it would not try to fight monsters - it would only run towards the repair place even though it was being attacked, so that when it dies, it will at least be that much farther away from the problem area.

Thanks for posting that custom bot code! I haven't seen bot code before, and look forward to trying it out and tweaking it for my own use.
 
Last edited:
On my shammy it's been working really good, not perfect, but extremely good as a healbot (thx to the Squire plugin).
Although, on my hunter, as it have been stated many times, doesn't do much until ~10 sec after pet attacks (= Really close to being suicide botting). I love this bot (HB), it's extremely good but PvP wise it could have som improvements. Maybe scanning certain areas (for AB example) and report in /BG (3 incoming Stables et cetera).

Grinding rox, IB rox even in this state, Questing is beta but it's still pretty good.
 
Last edited:
Back
Top