Welcome back.
Now our abilities have the concept of a level requirement and we've created a gameplay ability electrocute
which really does nothing.
It prints a string and ends itself after one second.
But in our class defaults its ability tag has been set.
We have an ability tag of abilities lightning electrocute.
Not only that in our ability info we have an entry for electrocute which has a level requirement of
two.
Now that means when we level up our spell menu should show this icon change because it's assigned to
electrocute.
So if I were to, let's say, kill an enemy, level up now that I'm level two, I expect this to show
at least eligible rather than seeing the locked icon.
I want to see the lightning bolt.
That's what we're concerned with now.
So how are we going to do that?
Well, our ability system component has access to all activatable abilities as well as the capacity
to give abilities.
So I'm going to close the editor, I'm going to close all tabs and I'm going to open up my ability system
component or a ability system component.
I'd like a function on this class that we can call to update our statuses, basically to go through
those ability info entries and the ability information array in our data asset and see if we meet the
level requirements for any of them.
So I'm going to make a function here on the ability system component to update ability statuses.
It's going to be void, I'm going to call it update ability statuses and it's going to take in an INT
32 called level.
So it needs to know what level to check against our level requirements.
Let's go ahead and generate the definition and what is this function going to do.
It's going to need to get ability info.
So I'm going to use you or ability system library that results in the header include and we're going
to get ability info.
This requires a world context object.
I'm just going to call get Avatar actor and pass in that.
Now the ability info is something we can store in a local variable of type you ability info and I'll
call it ability info.
So what am I going to do with ability info?
Well, I'd like to loop through its array because we have an array in ability info.
Let's just go look at it.
It's in data ability info dot h and it's called ability information.
So I'd like to loop over this ability information because each struct in ability information has the
level requirement that I need to check.
So here in the ability system component, I'm going to loop in a for loop.
For each f or ability info.
This can be a const reference an ability infos ability information t array.
And I need to give it a name.
I'll call it info.
So for each info I want to check the level requirement.
Now what happens if we find an ability in ability info that we've already given?
Perhaps one of the startup abilities like firebolt.
Well, we want to do nothing in that case.
So how do we know if we've done that?
Well, it'd be nice if we could check to see if we have that gameplay ability already.
Now, luckily the ability system component already has an array of activatable abilities.
We can check that array.
Now a clever way I'd like to do this is by creating a handy function that will return the gameplay ability
spec based on the ability tag.
And if we don't find one, we know that that gameplay ability hasn't been given yet.
So I'm going to make a useful function in our ability system component.
I'm going to put it up here next to our other get functions.
It's not going to be static, but it's going to return an gameplay ability spec, but I'd like to return
the spec as a pointer and we'll see why in a second.
I'm going to call it get spec from ability tag.
So this will take in a const gameplay tag by const reference called ability tag.
So this function will be designed to return a pointer to a game playability spec corresponding to this
ability tag.
If this ability system component has that ability with that tag, otherwise it'll return a null pointer.
So that's why I'd like to return a pointer as we can perform that null check and we'll consider a null
pointer to mean that we don't have the ability with that tag.
So let's go ahead and generate this definition and this function is going to loop over our activatable
abilities, which means we want a scoped ability list lock.
We want to lock it so that we don't run into complications from abilities being added or removed or
changed while we're looping over them.
So to create a scoped ability list lock, we've done this before.
We create an scoped ability list lock.
I'm going to call it active scope lock.
We initialize it with the ability system component.
So we dereference this and pass that in.
Now we can loop over our activatable abilities.
These are gameplay ability specs, so I'm going to use gameplay ability spec references.
I'll call each one ability spec and we'll get activatable abilities.
So we're looping over each ability spec now for each ability spec we need to get their ability tags
and loop over those.
So we're going to make another for loop.
This is for gameplay tags.
Each one will be called tag and we're going to loop over ability spec dot, ability dot get as.
That's a pointer wrapper.
Right?
It's a t object pointer.
And from this ability we're going to get ability tags and we'll check if the tag matches the tag we've
had passed in.
So we're going to say if tag dot matches tag ability tag.
And we'll return the ability specs address because we're returning a pointer after all.
So we'll use the address of operator and return a pointer.
If we reach the end of this, we return a null pointer.
So now we have a nice function to return the ability spec pointer based on a gameplay tag.
We can really use this to check to see if a given ability with this tag exists on our ability system
component.
So back to update ability statuses as we're looping over all of the info in ability info's, ability,
information array, we can check to see if we can get a valid pointer using this gameplay ability tag.
We can say if and we can call get spec from ability tag.
Passing an info dot ability tag.
And if that's a null pointer.
Then we know that we've stumbled across an ability that is not already here in our ability system components,
activatable abilities.
If it does not return null pointer, it's already in our activatable abilities and we don't need to
do anything.
We can just go to the next iteration of the loop.
Now if it's null pointer, that means it doesn't exist in our activatable abilities and we need to check
its level requirement.
In fact, we can check its level requirement before even bothering to do this, which would be even
more performant.
So what we'll do is we'll check if level the level passed in is greater than or equal to info dot level
requirement.
If so, then we can continue working.
We can put this inside of that.
Now, if you don't like the nesting inside of the nesting here, we can move to the next iteration of
the loop.
If level is less than level requirement, I tend to like less nesting.
So I'm going to bring this back out and I'll just continue to the next iteration of the loop with continue
if level is less than level requirement.
So we're looping over our ability information.
If level is less than level requirement, just continue to the next element in ability information.
Don't do anything else.
Now, another thing is we can also continue early if our infos ability tag is not valid.
So we can also say if info ability tag dot is valid and put a big fat not here.
We can continue in that case to.
And the spec from ability tag returns null, which means we don't already have that ability.
Then finally we can give that ability.
That involves making an ability spec from the ability in that info.
We can say f gameplay ability spec, we'll call it ability spec.
And we'll make one F gameplay ability spec and we'll make one using the ability in info because we now
have info ability that subclass of.
And since we're giving it for the first time, we'll give it at level one.
Unless of course we're loading in from disk.
Then we would load in that ability.
But we're not saving and loading just yet, so hang tight on that one.
Now that we have the ability spec, we can give the ability, but not before first setting its status.
Right?
And since we're updating ability statuses, we know that because that ability hasn't been given yet,
we're going to consider it to have been locked.
And now that we're leveling up and this ability is becoming eligible, we're going to give it that eligible
tag.
We're going to take ability spec, we're going to take its dynamic ability tags, we're going to call,
add tag to it, and we're going to add F or a gameplay tags.
We're going to get it and use abilities status, and it's going to be eligible now.
So we're now changing its status.
Okay, so that's pretty cool, right?
We're setting its ability status to eligible.
And finally after that, we can give the ability by calling, give ability, passing in ability spec
and we'll have the ability, but it'll be eligible so we won't be able to actually activate it yet.
But at least we have it and it has a status.
Now, as soon as we add an ability and change something on that ability, there's a way to force it
to replicate right away.
We do that by calling mark ability spec dirty and passing in that ability spec.
So if you ever want to force an ability spec to replicate now instead of waiting till the next update,
you call mark ability spec and we're going to do that as it's important that our clients know right
away if this change has occurred and if we do that, then we can start thinking about making a broadcast
to our widget controller, our spell menu widget controller, so that it can update itself.
So for now, I think this is a good stopping point.
And the next video we're going to make a delegate we can broadcast when an ability status has changed
and then our spell menu can update its widgets accordingly and we can start seeing icons in our spell
tree for abilities that are now eligible.
So we'll do that next.
I'll see you soon.