All right.
Welcome back.
Now in our widget controller for the overlay, we have a to do and on initialize startup abilities,
we want to loop through all of our ability system components, activatable abilities, and we want to
look up their info from our data asset ability info.
Now I don't want to reach in and get that gameplay tag container and loop through it right here.
I'd rather do it in a more self-contained way where we don't have to expose the contents of the ability
system component so much.
So what I'd rather do is make a delegate that I can bind a callback or a lambda to and pass that over
to the ability system component, which can then loop through all of its activatable abilities and do
something for me.
So here's what I mean.
I'm going to go over to aura ability system component and make a delegate for each ability.
So I'm going to declare and I'm not even going to make this multicast, I'm just going to declare delegate
and it's going to be one param.
Now I'm going to call this for each ability and it's going to take a single input.
It's going to be a const reference to an gameplay ability spec.
Because as we loop over all of our activatable abilities, those are ability specs.
So I want a way to loop over all of those abilities.
In other words, I want a function on the ability system component that can take in one of these for
each abilities, which will have a function bound to it and loop over all of my abilities and call that
function that's bound to this delegate.
So I'm going to create that function here.
It's going to be public and I'm going to call it for each ability, void for each ability, and it's
going to take this delegate for each ability and it's going to take it by const reference and I'm going
to call it simply delegate.
And what does for each ability need to do?
It needs to loop through all of its activatable abilities.
So we'll have a for loop.
We're going to say for const F gameplay ability spec const reference of course, and this will be called
ability spec and we're looping through all of our activatable abilities.
We can get those with get activatable abilities.
And what we're going to do is if that delegate has a callback bound to it, we're going to execute that
delegate.
So we'll take the delegate and call execute and we can call execute if bound and pass in the ability
spec as this delegate requires the ability spec now execute if bound will return a boolean.
If the delegate has no callback bound to it, then we can get that false value.
So we'll say if and we can say if not execute if bound.
So this will still execute it if it is bound.
But if it's not bound we'll get that false value returned.
And in that case we can log, we can log an error, we can say Yui log, we can use our new fancy log
aura.
Remember autocomplete included my aura log channels when I typed that we're going to log an error and
it's going to say failed to execute delegate in.
And if we want to log the name of a function, we can use percent s and what are we going to pass for
that double underscore function?
Double underscore.
This is a nice handy way to just log the name of the function we're in for each ability.
Pretty nifty.
So we now have this for each ability and it loops over get activatable abilities.
Now there's another reason for doing this in the ability system component and not in the widget controller.
And that is as we're looping over abilities, we have to be careful because abilities can change status.
They can become no longer activatable for example, maybe they're blocked by a certain gameplay tag,
etcetera.
So a good practice when looping over this container is to lock that activatable abilities container
until this for loop is done and we can do that with F scoped ability list lock and create an active
scope lock.
We're going to call it active scope lock.
And this requires the ability system component, not by pointer but by reference.
So we need to dereference this and pass it in.
So with this we'll be safe to loop over all of our activatable abilities because the list will be locked
and it'll keep track of any abilities that are attempted to be removed or added and wait until this
scope has finished before going and mutating that list.
So this is a safe way to do it and this is something I'd rather just handle here in the ability system
component rather than from outside of it makes sense.
Okay, so now we have this for each ability and back in our overlay widget controller, I'd like to
do something for each ability.
So what I'm going to do is create a lambda to bind to for each ability.
So we'll make an F for each ability here called, I'll just call it broadcast delegate and I'm going
to bind a lambda to it.
Broadcast delegate dot bind, lambda and what can I bind to it?
I'm going to bind a lambda.
So we're going to use square brackets, parentheses and a function body like we usually do.
And I'm going to put those curly brackets on their own lines so we can kind of better see what the function
body looks like.
I can go ahead and keep that closing parentheses there.
And here I want to do something.
For each ability.
Now, what I want to do for each ability we have the to do right here, we want to get information about
all the given abilities by looking up their ability info.
Now our ability info.
It's a variable here on the widget controller and it has that function we made find ability info for
tag.
So if we have the tag for a given ability, well then we can get its ability info.
But all we really have here in this lambda is the ability spec.
That's right.
We need to make this lambda have the correct signature if it's going to be bound to broadcast delegate.
This can only take something that can receive a const gameplay ability spec reference.
Right?
We're going to call it ability spec and here in our lambda we need to figure out how to get that ability
tag from the ability spec and in addition ability info can't be accessed from within this lambda if
we don't capture this right.
But still we need a gameplay tag to pass in to find ability info for tag.
So let's make another Todo here that says need a way to figure out the ability tag for a given ability
spec.
So how are we going to manage that?
Well, as long as all of our abilities have a given tag in its ability tags, we can check if it has
a parent tag and its tag hierarchy.
So we could create a nice utility function on the ability system component for getting the tag for any
given spec.
So we're going to go into our ability system component and make a nice utility function that can do
that.
It's a function that I'd like to return and gameplay tag from and I'm going to call it get ability tag
from spec and it's going to take in a const gameplay ability spec reference called ability spec and
it's going to return its ability tag.
So let's go ahead and create the definition for this and it's going to be relatively simple.
We need to get the ability tags from this ability spec well to get the ability tags, we take the ability
spec and it has a ability which is the actual ability and this ability is a t object pointer.
So we can call, get on that and from that pointer we can get ability tags.
So kind of a lot of indirection here and we should definitely make sure the ability is a valid pointer.
So we'll say if ability spec dot ability, then we'll get the ability tags.
And this is a gameplay tag container, so we're going to loop through it.
We're going to say for gameplay tag tag in this container.
So we're going to drag that up in here and what are we going to do for each tag?
We want to see if it's an ability tag so we can say if tag dot matches tag and we're not going to use
matches tag exact, we're just going to see if it contains ability in it.
So we can say if gameplay tag request gameplay tag using the name of abilities.
So basically for a given ability spec, we're going to look at that ability specs, ability tags and
assuming it only has one ability tag that has abilities in it, then we're going to return that tag
and if we reach the end, we're going to return an empty gameplay tag.
And if you wanted to, you could log an error in that case.
But I'm okay with just returning the tag if it's there.
So now we have a nice function for returning an ability tag.
In fact, this can even be a static function because it just takes in something and returns something.
So really this could be marked static like that.
Now that we have this handy function, let's go back to our widget controller where we have this line
commented out where we're trying to find the ability info for a given tag and we have its spec.
So we need to take our aura ability system component and call get ability tag from spec passing in our
ability spec.
Now we're trying to use aura ability system component in our lambda and that is not captured either.
So we have to capture that.
It's just a pointer.
We can capture it by value, that's totally fine.
And now that we have our ability tag, now it's possible to find the ability info.
Now we have an aura ability, info struct.
We're going to go ahead and create a local for ability info.
We'll call it info.
So now what do we do?
We have the info.
We want to broadcast that up to our widgets.
Right?
But we have to make sure it's all filled in completely.
For one, we're not setting its input tag and we don't know what its input tag is.
So yet again, we need a nice way to get that from our ability tag.
Remember, the input tag is one of the dynamic ability tags.
When we give the ability, we're adding to its dynamic ability tags the start up input tag.
But that's only for our default abilities.
Soon we'll be equipping abilities dynamically and we'll have to assign input tags to those.
So I'd like another utility function to be able to get the input tag from the ability spec.
So just like get ability tag from spec, we're going to have get input tag from spec.
So let's make another function that returns an F gameplay tag.
We'll call it get input tag from spec and just like get ability tag from spec, it's going to take in
a const reference to the ability spec.
Let's go ahead and just really quickly make this function.
So how is this going to work?
Well, we're going to loop over our dynamic ability tags this time because we know that the input tag
is in that gameplay tag container.
So we're going to say for F gameplay tag tag in ability spec and from the ability spec, we can get
dynamic ability tags directly.
We don't have to get it from the ability and with that we can match the tag.
We can say if tag dot matches.
Again, we're not using matches tag exact, we're just going to match against the input tag.
So we're going to use f gameplay tag request gameplay tag f name.
Input tag.
And again, we should only have one input tag at a time.
So just like our ability tags and we can go back to or a gameplay tags and remember we have our input
tags, right?
Input tag, LNB input tag, R&B and so on.
So we're going to return that tag.
It's going to be return tag.
And if we never return the tag, we have to return an empty gameplay tag like so.
So now we have get input tag from spec and now we can call that to get our input tag so we can come
back to our widget controller and we can take our info and set its input tag by getting our ability
system component or a ability system component and calling our new function get input tag from spec
passing in the ability spec and now our info struct has its input tag set and that's all great.
Now the last thing we want to do is broadcast this for ability info.
In other words, we need some kind of delegate to broadcast so that our widgets can respond to it.
We need an ability info delegate.
So yet again, we're going to make another delegate up here in our overlay widget controller that can
broadcast ability info.
Now I'd like this to be a dynamic multicast delegate as this will be bound to in blueprints and multiple
blueprints to boot.
So we're going to say declare dynamic.
Multicast delegate and we're going to broadcast with one param because we want to broadcast that info
struct and I'm going to call this F ability info delegate.
Actually we can call it info signature and then we'll create a delegate of this type.
And of course it needs to take one input parameter.
We're going to use const f ora ability info.
That's going to be forward declared because this is a reference and we can do a comma with the name
of that input parameter.
And now our ability info signature can be added as a blueprint.
Assignable.
We're going to call this ability info delegate.
It'll be blueprint assignable and we can put it in the category gas messages and our overlay widget
controller can simply broadcast ability info.
Delegate passing in info.
Now, all of this is just inside of a lambda which is being bound to broadcast delegate.
We want to do this.
We want to call this lambda for each ability, right?
That's why we created that for each ability function on the ability system component.
So we're going to take our ability system component for each ability call that function passing in our
broadcast delegate So when on initialize startup abilities gets called, we create a broadcast delegate
and bind a lambda to it.
That lambda creates an aura ability info which is set by calling find ability info for tag on our ability
info data asset.
We need the tag for each of our ability specs and our lambda can receive an ability spec by const reference.
So find ability info for tag needs that tag, which is why we created a function on the ability system
component to get the ability tag by looking up an ability spec.
Once we get that info we now have the aura ability info, but we need to set its input tag.
So we created another function on the ability system component that can take an ability spec and look
up its input tag from its dynamic gameplay tags.
Once we have that, we set the input tag on that info.
It's very important because we need to know which input tag is associated with which ability.
So once we have that info struct, we're passing it into broadcast, broadcasting it so our widgets
can then receive that broadcast and then do their own thing with that info After defining the lambda
and binding it to our for each ability delegate, we call for each ability on the ability system component
passing in that delegate and then the ability system component in for each ability is going to loop
through its activatable abilities using an active scope lock and execute the delegate that we've passed
in.
So we're passing in one delegate and it'll be executed for all abilities in our Activatable abilities.
Now get input tag from spec can also be marked static, so these two handy utility functions can both
be static.
So making sure that is there.
And now things are all in place and the only thing left is to go into our widgets and register some
kind of event to bind to the ability info delegate so it can receive that ability info.
Now another thing is here we are creating a lambda and passing it into a function.
So recall that we've done this before and we could easily replace that lambda with a function pointer.
So that's something that I leave to you as an optional exercise if you'd like to do that.
But for now, I think we're ready to start binding events to our delegate.
We're going to broadcast for each of our abilities and we'll do that next.
So excellent job.
And I'll see you in the next video.