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

Question Regarding Spell Composites

benkoren

New Member
Joined
Jan 18, 2011
Messages
356
Reaction score
28
Consider the following code:
Code:
SpellManager.CreateSpellCastComposite(spell, reqs, location);

This returns a composite that will cast the given spell if the requirements are met. I am currently working with custom cooldowns implemented with timers. I need to be able to reset those timers if and only if the spell is actually cast. The way this is done in the Exile CR is the timer is reset during the calculation of "reqs". Are all requirements across the entire PrioritySelector evaluated on every tick? Or are they evaluated one by one until an action's requirements are met, the action is executed, and the tick ends? Unfortunately my testing points toward the former - please let me know if I'm wrong.

Regardless, there are definitely many instances where you provide reqs that evaluate to true but the spell is not actually cast. Since the timer was set during the calculation of reqs and not after the spell was actually cast, we bot needs to wait the duration of the timer to even attempt the cast again.

I have attempted to get around this by doing what the flask logic does: use a decorator with a set of requirements and a corresponding set of actions. The flask logic uses the Use() method in the action, but I cannot figure out how to do the equivalent with a spell. I have tried to use SpellManager.Cast(), but this does not seem to work. Here is an example:
Code:
private Composite CreateDefenseLogic()
{
  return new PrioritySelector(
    new Decorator(ret => _wallCd.IsFinished,
      new Action(ret =>
      {
        SpellManager.Cast("Frost Wall", MainTarget);
        _wallCd.Reset();
      })
    )
  );
}

So I guess it all boils down to this: How do I run some my timer logic if and only if a certain action (spell cast) is selected by the priority selector and it is successfully performed?

Thanks,
Ben
 
Last edited:
So I guess it all boils down to this: How do I run some my timer logic if and only if a certain action (spell cast) is selected by the priority selector and it is successfully performed?

This issue is exactly why the bot has issues with skills. In the old code, which is when the CR was written, our API tried to perform actions directly. Casting a skill would call a client function to do so. Unfortunately, that led us to seeing a higher rate of bans/flags, most likely due to the client doing things it shouldn't have been.

In our rewrite, we changed the approach we took for interacting with the client, which seems to be the only way possible to do things without breaking the client. However, even though we just did a huge rewrite of a lot of things, even more things need to be rewritten now to keep up with how the game is changing.

The process for casting a skill would be as follows:
1. You move your mouse cursor to the desired location or on top of the target.
2. You press/click the action to perform the skill.

Now, that sounds easy, but doing that via code is a lot more complicated (which this game is filled with such complexities).

Implementation wise, this is what those 2 steps above translates to:
1. Get the location/entity to use a skill on.
2. Map the world location to the screen location.
3. Move the game cursor to the screen location.
4. Wait a tick for the game to update the game cursor and do the regular client code (hit testing)
5a. If the mouse location is "close enough" to where you want to cast, then you can perform the action.
5b. If the entity you wish to interact with is actually highlighted by the client, then you can perform the action.
5c. Otherwise, you have a bunch of error handling logic to perform (will get into this in a bit)
6. Perform the input action. However, due to the way input works in this game, it's possible the client can not process the message (or just ignores it since something else is going on). Part of our internal code does something really specific to help deal with this though, but there can be side effects.

Let's say you get through to step 6, at which point now you need to wait a little for the action to be performed (which it might be, or might not be). In our api we have the ability to see which object is doing what action, but it needs to be extended some to be able to handle it a bit better (this has been on the todo list, as we wanted to make an aggro table for more advanced logic, etc...) Assuming it was all there, the process would go something like:
1. Check the actor's current action.
2. If it's null, wait or time out if you've waited too long. How long you actually have to wait will vary, once again due to how the game works.
3. If it's not null, check to see if it's the action you want. If it is, you know the skill is being used, and the cast was successful.

The important thing here is that, you're going based on what the client is saying. In this game, skills are cast client sided first, and then a request is sent to the server. This is why desync gets to be so bad, the client is running its own game simulation. It's possible for your client to think it's casting a skill when the server would disallow it, which means you can never have an accurate system, just something that was "close enough". With the overhead introduced into running the bot in the out of process way we do, desync issues are more prevalent, but that is the trade-off of having the power we provide.

Now, all this is leading into your issue with behavior tress. The problem now is that, for anything to work in the client, it cannot be done in 1 frame; it has to be done over a span of many frames. Doing that in a BT, is really annoying because the design is now overly complicated with a bunch of state variables as you need to resume execution at certain points rather than re-execute the current child composite (RunStatus.Running). Instead, coroutines provide a much better solution for the issue, because you can inherently wait as long as you need in a procedural execution of logic to guarantee things are going to execute just how you need them to, and it's way easier to work with and code.

The bot right now is a mix of BTs and coroutines, as well as a lot of old api code that used to be applicable for the client, but is not anymore. Pretty much all of that stuff needs to be revamped to better fit what we have to do in this game. What we have now mostly works, but it's by no means where we'd like it to be, or even that user friendly for developers, but that's simply because of the hoops we have to jump through to bot in this game.

So to give you an answer to your question, there's not much you can actually do right now with what we provide to accurately do what you want, because there's no direct way to perform an action, only indirect, which is highly dependent on the client.

The errors in 5c are the random issues you see with the bot when it comes to getting stuck, or being unable to open chests in clusters, stair issues, etc... The way this game client works, there are client issues that are happening all the time, but they are hidden by the fact a user subconsciously is able to do the right thing, whereas a bot's precision exposes those issues. As a result, when you go to try and perform an action, the client might be performing a different action (such as interact if you're using LMB) or target a different object (hit box testing pretty much contains anti-bot code in a sense) so you have to react to the client state after you perform the action, rather than just assume it's going to work (which a lot of our code was built assuming it would.)

When this current input system was rewritten, the goal was to provide a seamless api for the users to just do things, and only handle a few of the errors themselves, but we've come to see that is simply not possible generically speaking. Depending on what you are doing, you might not want to do some things while at other times, you do. For example, when moving, you care about where the game says the cursor is, vs where you want it to be. When casting a buff, you dont care where the cursor is as long as it's in the game client, but the current code cannot handle both of those cases internally with the code we have (which I've just explained the issue in the past few beta revision in another user's thread who had issues casting Molten Shell near a rock).

Hopefully that sheds some light on the current issues. We know what the problems are, it's just they are really tricky to solve correctly, as they require very specific logic which doesn't work generically.
 
This is all extremely helpful; thank you.

This afternoon I think I've worked around the limitations pretty gracefully and came up with a solution that doesn't do exactly what I had originally wanted but is good enough. For example, rather than limit my Frost Wall based on a cooldown (which didn't work well because of the multi-frame issue), I just have it limited to only cast if I have ample mana. The latter is actually more ideal.

The other thing I ended up doing was removing the wait timers on my totem casts. They fell into the same issues you stated above. If you're interested in seeing what I came up with (that's actually running quite well), I've made it publicly available here (with git repo and all):

http://www.thebuddyforum.com/exileb...152356-combat-routine-bens-totem-templar.html

Thanks again.

- Ben
 
Back
Top