Welcome back.
Now we're concerned with activating and deactivating our passive abilities.
Now we've discussed in the last video what that means.
If we equip one of these to our passive slot down here, it's going to activate.
If we push it out by replacing it with a new passive ability, it's going to deactivate.
So we need to figure out how to do that.
And on top of that we need to know when an ability has been activated and deactivated.
And for that reason, I'm going to open up my passive gameplay abilities and use event activate ability
and event on end ability.
So we're going to print strings here so we know when an ability has been activated.
Another thing I'd like to change is right down here in the details panel, our net execution policy.
Now we're going to be doing this from the server.
So earlier in the course I mentioned that server initiated is an ability instantiated by the server
but will also be run on the local client if one exists.
So this is one of those rare cases where you'd actually want to initiate the ability on the server.
As the server makes the determination to equip an ability, and equipping an ability is directly related
to activation for passive abilities.
So strangely it's appropriate for server initiated for the net execution policy.
We're going to do that for all three, and we're going to have a print string to know when the ability
has been activated.
So I'm going to make the print string I'm going to change its color to green.
And it's going to say Halo of protection activated.
We'll give it more of a duration, say four seconds.
I'm going to duplicate that node and use on end ability and say Halo of protection deactivated.
We'll just say ended.
Ended is more appropriate.
And we'll make this red.
In that case.
Now I'm going to copy these two Ctrl C and open up life siphon and life siphon can do the same thing
on activate, but we're going to say life siphon instead of halo of protection.
So we're going to say life siphon activated and life siphon ended.
Life siphon.
We'll connect those.
I'll go to my class defaults and set my.
Net execution policy to server initiated.
Compile.
Save and compile save.
We're done with those.
But we want mana siphon as well.
So control V here.
Hooking these up.
These will say, man a siphon.
Activated and ended.
And we'll go ahead and set in the class defaults.
Net execution policy to server initiated for this compile save and close.
We can save all now.
So the question is how are we going to activate these?
I'm going to go to one player.
How are we going to activate them.
When we equip one of our abilities we want it activated.
When it's unequipped we want to push it out.
So let's think about this.
We're going to need to go back to the C plus plus side.
So I'm going to open our ability system component.
We're back to server equip ability.
This is where it's all going to happen.
And I'm going to move this over to the right and get my header file right there just next to it.
So we are going to need to think about what's going on here.
Now the first thing we're doing is we're getting our gameplay tags.
That's good.
We're storing the previous slot, but this is actually the previous slot in a sense of what slot did
this particular ability have before before we went in and changed things.
Right.
So this is really just the previous slot that we're remembering.
If we're equipping an ability that had a different slot in the first place, so we can go and clear
that previous slot.
We also have the status of the ability, and we're checking that status to make sure it's valid here.
We're seeing if the status is equipped or unlocked if that's the case.
We can then move on and we don't want to do anything if the status isn't valid.
So that's all good.
Then we do all this stuff.
We mark the ability spec dirty and then call client equip ability so the spell menu can update itself.
Now it's this stuff that I'm concerned with.
We're going to need to basically replace this because this all has to do with offensive abilities.
It doesn't really take anything into account for passive abilities.
It clears all the abilities of this given slot.
It clears the current selected ability of its slot, which basically means that after calling these
two functions, we don't know which abilities are associated with which slots.
We don't know which ability might have been there before and how are we going to deactivate it if it
was passive in that case?
Right.
So all of this stuff is going to be moved on down, and we're going to actually comment it out for now
and get it out of the way.
We don't want to just delete it because, you know, as a general rule, I don't delete all my hard
work that I know works until I have something else that I know works, that I'm going to replace it.
Until then, we comment it out.
Okay, so what are we going to do?
We're going to handle activation slash deactivation for passive abilities.
That's on our menu here.
So we need some pseudocode.
We need to know first of all as we're clicking right we have an ability selected from our spell tree.
Either it's offensive or passive.
And we're clicking on our spell globes down in our equipped row.
If we're clicking on one is it empty or is it not empty?
In other words, is there an ability in this slot already?
Now, I'd like a function to check that.
I want to check if there's a slot.
In other words, I want a slot is empty function that returns a boolean that can take in a slot.
So why don't we go ahead and make that function?
We're going to be making a lot of little ease of use utility functions as we change this code so that
our code stays readable and looks good.
So right here these are basically our utility functions.
We're going to add more to these I'm going to have a bool returning function called slot is empty.
And it's going to take a const f gameplay tag reference.
And we'll call it slot.
And I think I'd like to refer to input tags as slots basically in all of these functions.
So even though we have something like get input tag from ability tag, I'd kind of like this function
to be called get slot from ability tag.
We can easily rename it by right clicking on it, going to refactor, selecting rename.
And we can call this get slot from Ability Tag.
Click next.
And now it's called get slot from ability tag.
So anyway we have a slot is empty function.
We need to generate the definition for it and decide how we're going to figure out whether or not a
given input tag is occupied with a gameplay ability.
How do we know that?
Well, we're going to need to check all of the gameplay abilities in our activatable abilities.
So we're going to say for gameplay ability, spec, reference ability, spec in get Activatable abilities.
Now, we've mentioned before that whenever we loop over Activatable abilities, we should make a scope
lock as we're doing here and get spec from ability tag.
We should make an active scope lock.
We should do that here, and we should also just make sure that everywhere we're looping through activatable
abilities, we're doing this because I know there are some cases where we're not.
So I'm just going to search for get activatable.
Abilities and just search through the entire file.
As here we have ability input tag pressed.
We're looping over it.
That's dangerous to not place a scope lock because abilities can be added and removed.
So we don't want that to happen in the middle of this.
So we'll place that lock there.
We see the same thing in ability input tag held.
We'll place a lock here as well.
And here in ability input tag released.
Same thing.
We'll place a lock there.
And then it looks like the rest of the file is behaving.
So I don't see any other loops without those locks.
But we definitely did a good thing there, making sure to place a scope lock for all of those loops
over get activatable abilities.
Now for slot is empty.
I need to know if this particular ability spec has a slot, right?
It has this slot we're passing in and its dynamic ability tags.
So in other words, we can return ability spec dot dynamic ability tags.
And we can check has tag exact because these containers have functions like that has tag exact needs
to know what tag.
We're going to check the slot.
And this right here alone could be a pretty useful function called ability has slot.
So I'm going to make that a function as well.
One that returns a bool ability has slot.
And it's going to take in the ability spec by const reference.
So f.
Gameplay.
Ability spec.
Const reference called spec and a const gameplay tag.
Reference called slot.
Important.
When you're passing in an ability spec, you don't want to make a copy of it.
You want the original, so a const reference is a good way.
A pointer is another good way.
So ability has slot is just going to return this.
Dynamic ability tags that has tag exact only.
We're going to use RSpec here.
And now we have this nice function ability has slot.
And I'd like to check that here.
What I'm going to do is say if ability has slot.
And we'll pass in that ability spec and we'll pass in the slot and we'll return true.
And at the very end of here, we'll return false.
If we don't ever make it there.
So this allows us to break out of this loop early, as soon as we find an ability that has this slot.
Okay, so now we have a slot is empty and we can use this.
So we can go back to where we were.
Right here.
We've made.
Our slot is empty.
How are we going to use it?
Well, I'd like to check to see if there's an ability in this slot already that we're dealing with that
we've passed up to the server.
So I'm going to say if not, slot is empty.
Now we're going to pass that slot in.
So if not slot is empty, we're going to say in a little side comment here, there is an ability in
this slot already deactivate and clear its slot.
So that means that we have an ability selected.
And we're clicking on a slot that's already got an ability there.
So we know the slot.
We know that it's not empty.
We also need to know what ability is there so we can deactivate it, at least if it's a passive ability.
Right.
And if we want to deactivate the ability, it would help if we knew which one it was so we can identify
it with the ability spec.
So I'm going to need a function that can return whatever ability spec is associated with a given slot.
In other words, we need some kind of function like get spec with slot, which can take in a slot and
return an ability spec.
So I'm going to make that function and I'm going to make it return a gameplay ability spec pointer.
That way we can always check if the pointer is null, and if it's null, then there is no ability spec
with this slot, right?
So I'm going to return an gameplay ability spec pointer from this function.
It's going to be called Get Spec with slot, and it's going to take a const gameplay tag by reference
called slot.
So let's make this function.
And how are we going to return the correct spec?
Well, we need to loop through the abilities and see which one has this spec.
So that means we need an EF scoped ability list lock.
We'll call it Active Scope Lock and it needs the ability System component.
So we dereference this and pass it in and now we can loop.
We're going to loop over F gameplay ability spec references.
Ability spec and get activatable abilities and I want to check in each abilities.
Ability tags and its dynamic ability tags, and I want to see if it has this given slot.
Right.
So we can take ability spec.
Dot ability tags.
Dynamic ability tags.
That is.
And we can see if it has a specific tag has tag exact and we can pass in the slot.
So as we're looping over the ability specs, we'll say if.
Ability spec dynamic ability tags has tag exact.
If that's the case, yes, then we'll return the ability spec.
But we have to return a pointer an address in other words.
So we're going to return the address of ability spec.
Now, if we make it to the end of the for loop, we return nothing.
We return a null pointer as dynamic ability tags doesn't have the slot for every single ability spec
in Activatable abilities.
So now we have get spec with slot a nice utility function.
Let's go back to what we were doing.
So let's get the spec.
I'm going to make an F game play ability spec pointer, and I'll call this spec with slot.
And I'll set it equal to get spec with slot passing in the slot.
So here we know that the slot that we're trying to occupy is already occupied.
Right.
And this is the pesky ability sitting in that slot that we want to push out.
Now this could be a null pointer.
So we're going to make sure that it's not.
So if spec with slot is not a null pointer, then at this point we know we have a valid slot there.
We're trying to replace it with a slot in our hand, and this would be a good time to see if they're
not the same ability.
Because if they are, well, there's nothing needed at this point and we can return out of this whole
functionally.
So I'm going to say if and we're going to take the ability tag we're messing with here and we're going
to call matches tag exact.
What are we going to match it against?
Well, we need to know the ability tag associated with this pesky slot that we're dealing with.
So we need to get ability tag.
From not spec but slot so we don't have git.
Ability tag from slot.
But we do have git ability tag from spec.
And we do have the spec.
So we're going to call git ability tag from spec that takes in a ability spec not by pointer.
So we have to dereference.
That's okay.
We made sure it's not null.
So we're going to say star spec with slot pass that in.
And in this case our ability tag that we're trying to equip into this slot as the same as the ability
tag in the slot, we're trying to equip an ability where it already is so we can return.
Now we don't just want to return though, because down here at the bottom, Client Equip ability is
taking care of some things in the spell menu side.
Remember, our spell menu has a little flashing rectangle down there waiting for us to click on it.
And if we click on it and the server just returns and does nothing, it's going to still be there waiting
for us to click on it.
We're going to keep clicking.
The rectangle won't go away.
It's going to be really frustrating.
So we just need to call client equip ability to make sure that that doesn't happen.
So we'll go ahead and get that there and return early.
So we can go ahead and put a comment here so we know what this means.
We'll say is that ability the same as this ability.
If so we can return early.
However, if we don't return early here, now we have some stuff to do, right?
And really the most important thing is, is this a passive ability?
Because this is handling things for the offensive abilities too.
So I'd like to be able to know if this is a passive ability.
I'd like an is passive ability function that can take in that spec.
So why don't we make an is passive ability that returns a boolean for us.
So back to our handy dandy functions.
We're going to make another bool.
Returning function is passive ability and it's going to take a const f gameplay ability spec reference
called spec.
And this is just going to tell us yes or no.
We can make this a const function.
And I'm going to generate the definition.
And how is this function going to work.
Well this one's a little bit more interesting.
Our ability type passive or offensive.
That's in our ability info.
We need to get the ability info if we want to check an ability type.
So this function needs to get you ability info.
So we'll get that data asset.
We'll call this ability info, make a local variable and use you or ability system library get ability
info.
Now it needs a world context object.
I always like to pass in the avatar actor.
It's just a habit and for the ability info we need to find the ability info for a given ability tag.
Well, we have a spec, so we need to get the ability tag from the spec.
We can say gameplay tag.
This can be const and we'll call this ability tag.
And we'll call Get Ability tag from spec because we have the spec and now we have the ability tag.
Now what we need here is the ability info struct that we can get from ability info.
In other words we can make a const f or a ability info struct reference called info and we can set that
equal to ability info.
Find ability info for tag passing in that ability tag.
And once we have that info we can get the type from it.
We can say const f gameplay tag ability type.
And we can get info dot ability type right.
No need for so many intermediate local variables by the way.
Just making things clear here.
What we're doing.
And this function returns a bool so we can return.
And what are we going to return.
We're going to take our ability type and see if it is the passive ability type.
So we can say if ability type dot matches tag exact f or a gameplay tags get dot abilities.
Type.
Passive and of course ability info could be a const pointer that is not const.
Const and we have an is passive ability function.
So now that we can know we can go back to what we were doing down here.
We have an is passive ability.
We can check that for the spec with slot.
This pesky spec that's sitting right there in the slot that we want to push out.
So we're going to say if is passive ability we're going to pass in that spec.
But spec with slot is a pointer.
So we need to dereference spec with slot pass that right in.
So spec with slot is a passive ability.
We're pushing it out.
That means we need to deactivate it.
And how do we deactivate it.
Well remember we made a delegate f deactivate passive ability here.
And our passive abilities all bind to this.
So we can broadcast the ability tag through this, and all of our passive abilities will respond to
it and check that tag.
So we're going to take deactivate passive ability.
We're going to broadcast it and we're going to broadcast the ability tag.
What's the ability tag for spec with slot.
Well we can get ability tag from spec and pass in the Dereferenced spec with slot like that.
So this is going to result in our passive ability with this tag ending itself.
That's exactly what I want to do.
So once we've done that, now that this ability in this slot has taken care of ending itself, now we
don't care what its ability tag is, we don't care what its input tag is.
In fact, we should clear its input tag of the slot that it was in, and we have a function to clear
a slot if we search for clear slot.
We saw that we were using it down there and it's going to take a spec get its input tag.
Check its dynamic ability tags and remove that tag.
It also marks the spec dirty to force it to replicate.
But we're going to be doing that already.
So I think I'm just going to remove that part from it.
And we'll just have clear slot here, which by the way, can be made static.
Now that we've moved that member function call out of it.
So clear slot here it is.
Down here we can make it a static function.
And going back to where we were right here after we've deactivated the passive ability, whether or
not it's passive, we still want to clear the slot so it doesn't have that input tag anymore.
So we're going to call Clear Slot.
And we're going to pass in that spec and clear slot takes in the spec as a pointer so we can just pass
that pointer on in.
Okay, so this takes care of the case where the slot was not empty.
So what do we do when we've passed that point?
Because that just takes care of pushing that slot out, right?
Now we need to put our new ability in that slot.
Now here's the thing.
Now that this ability could be passive, we need to activate it if it's not already active.
It doesn't have a slot already.
That's why I thought it would be nice to have an ability has slot function.
We made that up here as a useful function that one of our other functions can use.
Here we can check that.
We can say if not ability has slot.
Now this takes the ability spec.
We have a pointer called ability spec.
Now ability has slot is going to return true.
If the ability has a specific slot right.
We'd have to pass in the slot that we're checking.
But I want to know if the ability has any slot, not just a specific slot.
I want to know if it doesn't have a slot at all.
So in other words, I want a function called ability has any slot.
So I'm going to make another function actually.
So next to ability has slot I'm going to call this ability has any slot.
And this will take in just an ability spec, but not a slot because I want it to return true if it has
any slot, no matter what it is.
And when I say slot, of course we're talking about input tags, so we need to return our spec dot dynamic
ability tags.
Dot has tag but not has tag exact.
We want to check has tag and just check for input tag.
We want to return true if it has any tag with input tag in it.
So I'm going to use F gameplay tag request gameplay tag using the F name input tag.
Now yes, we're using an F name and variables are better and we could just go into gameplay tags and
make an input tag.
Gameplay tag.
Yes, you could do all that, of course, but I'm not.
I'm just going to do it this way.
I think it's totally fine in this case.
And this can be a static function.
In fact, ability has slot and ability has any slot.
Both can be static functions.
We'll go ahead and make them both static.
And with that we have ability has any slot.
So let's go back and we're going to use ability has any slot.
And we'll make a little comment here.
If not ability has any slot.
This ability doesn't yet have one.
So we're going to say ability doesn't yet have a slot.
In other words it's not active.
Now that's important because if this is a passive ability we're going to want to activate it.
So I'm going to check if is passive ability passing in ability spec.
Dereferenced.
Of course.
Then I want to try activate a.
And I can pass in ability back.
But it's got to be a handle.
So we need to pass an ability spec handle here.
So with that, now we're activating abilities and deactivating them based on the correct conditions.
If the ability doesn't yet have a slot, we know that it's not active because we're activating it every
time it's being put into a slot for the first time.
Now that takes care of activating it.
But what about its input tag?
It now needs to have a tag.
So we need a function to assign a slot to an ability.
So something like assign slot to ability.
Which can take in a spec and a slot, because this can handle not only removing any input tags that
it may have, because even though if we're in this if statement, it's the first slot it's getting,
it doesn't have one already.
But we're not in that if statement.
This only takes care of that case.
But in the other cases where there might be a slot already on this ability, we need to clear that and
then add the new slot.
So I'm going to make a new function.
It's going to be void.
And I'm going to call it assign slot to ability.
So this needs a couple of things.
It needs an F game play ability spec reference.
Now we're changing it.
So it's not going to be const.
And we're going to call this spec.
And a const gameplay tag reference called slot.
So assigned slot two ability is going to first of all remove its input tag if it has one, and then
assign it a new slot.
So we already have a function to clear a slot.
We can take clear slot.
We can pass in our spec.
Now clear slot needs a pointer.
So we have to pass in the address of spec.
No problem.
We have the address of operator to help us out.
Now we also want to add this new slot to the dynamic ability tag.
So we're going to take spec dot dynamic ability tags call add tag and pass in slot.
So two things out with the old in with the new.
And we have a nice new function that can be static.
So we can make this a static function as well.
And with that we can go back to what we were doing down here where we have assigned slot to ability.
We can call that function now.
So we have assign slot to ability takes in a spec.
We're going to pass in the Dereferenced ability spec and we're going to pass in the slot.
And after all that we can mark the ability spec dirty.
And after all that we can call client equip ability.
In other words we can delete these old comments.
And now we have a new algorithm.
And this algorithm should handle activating and deactivating abilities deactivating via a broadcast
of a delegate.
And those abilities should all be bound to it.
So with that, let's hit the debug button and launch the engine.
Okay.
So first thing I'm going to try with one player first.
And I'm going to go ahead and I need to level up.
So let's get some XP.
Level four now got three spell points.
I'm going to spend one and equip this one.
And look at this halo of protection activated.
So far so good I'm going to go ahead and spend a point on my next spell here and equip that down here.
Look at that life siphon activated.
So activating these abilities is working.
Let's see what happens when we take a new one.
Spend a point on it and push one of these out.
Let's push out Halo of protection.
Now this one is called mana siphon.
So we should see mana siphon activated and Halo of Protection deactivated if all goes well.
So I'm going to click equip and click on Halo of Protection.
Looking up to the top left of my screen and mana siphon activated.
But Halo of Protection was not deactivated.
So we need to find out why.
So activating abilities is working.
It's deactivating that we have an issue with.
So we need to find out if we're actually broadcasting this delegate.
I'm going to place a breakpoint there.
I'm going to level up.
And I'm going to go ahead and equip Halo of Protection.
And I'm just going to try to push it out right away with life siphon.
Okay, so I didn't get my breakpoint.
So that tells me we never made it this far.
And if I scroll up, what could prevent me from making it into here?
Well, now I've got my eye on slot is empty, so I'm going to go ahead and go back to that function
and make sure I didn't make any silly logic mistakes.
So the function is called slot is empty, which means we want to return true if there are no abilities
that have this slot.
Well, we're checking if ability has slot and looks like we're returning.
True if it does, but we want to return true if none of these have the slot.
So my true and false is are reversed, I need to return false if the ability has this slot.
So if any of our abilities that we stumble upon happen to have this slot, we can return false because
the slot is not empty.
Otherwise we'll return true if we get to the end of this.
And then our slot really is empty.
So for that reason I'm going to fix that and I can hit rerun.
And we can try this again.
All right.
So I'm going to go ahead and level up again.
Get out of here, Gabby.
And I'm going to go ahead and equip Halo of Protection.
There we go.
It's activated.
And I'm going to try to push it out first thing with the red ability clicking on here.
And there we go.
Halo of protection ended life siphon activated.
That was what I wanted to see.
Let's go ahead and full screen this because there are some pretty small icons down here.
But let's test the others.
Let's get our mana siphon, spend a point on it, equip it.
I'll put it down here.
Mana siphon.
Activate it.
Cool.
I'm going to select Halo of protection.
Equip and replace.
Life.
Siphon.
Life.
Siphon ended.
Halo of protection activated.
Exactly what I wanted to see.
Let's try equipping Halo of Protection to the slot that it's already in.
Okay.
Nothing happened as far as activation or deactivation of abilities, but because we called client activate
ability and went ahead and pushed along that same data down, at least we could end that animation,
play a little sound, and get back to square one.
Now I'm going to go ahead and just try different combinations and make sure that we're ending abilities
when we should and activating abilities when we should.
So let's just see what happens.
I'm going to take the blue and click on the red.
We should be pushing the red out and pushing the blue up.
So the red should be deactivated.
That's life siphon and the blue should not be deactivated and it should not be reactivated.
Nothing should happen to it.
Let's see what happens.
And we just see life siphon ended, which is exactly what we should see.
So pressing the red ability and clicking on the blue now should deactivate the blue.
We should deactivate Mana Siphon and activate Life siphon.
Let's see if that happens.
Okay life siphon was activated, but man is siphon was not so man is siphon needs to be deactivated.
And in this case we clicked on a slot that was not empty and we didn't get to deactivate it.
Let's see if we can get the blue to push out the red.
So that worked.
But when the red pushed out the blue.
Looked like it didn't do anything.
Let's just make sure that mana siphon does have.
Oh, there there's the issue.
So I suspect it was working, but my print string was not connected.
Let's try that again.
So got to level up first.
And what I'll do is I'll spend a point on all three.
I'm going to full screen here and we'll equip mana siphon.
Mana siphon activated.
And we'll push it out with life siphon.
Now we see mana siphon ended.
Life siphon activated.
So it's looking like everything is working as it should.
We're now activating and deactivating our passive abilities.
And they stay active.
They stay active as long as they're right here in our slots in the HUD.
So as long as we see them there, they're active.
Now, that's not exactly enough for me to just see icons and just know that these abilities are active.
I like to see particle systems.
This is Unreal Engine after all.
We have Niagara, we have some of the best particle systems on the planet, and it'd be a shame not
to take advantage of that.
So my next steps for this are.
I'd like to assign particle systems to play while these passive abilities are active.
And that way we can have something cool to look at.
And also visually, we'll know when halo of protection is active, when mana siphon is active versus
life siphon.
And if we swap them out, I want to see those particle systems swap out as well.
So that's our next step.
I'll see you soon.