Now in this video, we're about to make some really cool stuff happen.
As soon as we click on one of these and click on Equip, our spell menu is waiting for us to click on
one of these.
What happens when we do that is going to result in making some significant changes to our abilities.
We're going to either change their input tag or we're going to assign a new tag to them and they're
now going to be something we can activate.
So this is a big step in our RPG game and we're going to make it happen.
Now with this step comes a couple of complexities.
For one, we need to handle certain things.
When we select an ability that's already in our spell row and hit, equip and then click somewhere else.
Well, now we need to reassign that input tag and we have to clear where it was before.
In other words, we need to remember where it was.
In that case, so we can go back and clear it.
That's a little bit of a challenge.
We also want to make sure that when we change down here, we need to change out here as well, because
both of these are a reflection of our abilities and they need to be consistent.
So we'll keep all these things in mind as we accomplish this feat.
Let's close the editor and let's make this happen.
The first thing I'm going to do is go back to equip button pressed an equip button pressed.
I'm going to check to see if our currently selected ability has the status of equipped.
And if it does, I'm going to save its input tag so we know the slot that it's in.
So I'm going to make a gameplay tag variable to store the selected slot.
I'm going to call them slots while I'm in the spell menu because these are the input tags that have
been assigned a slot in the menu.
Make sense.
So I'm going to make a private gameplay tag.
So f game play tag and I'm going to call it selected slot.
Now selected slot is going to be set as soon as I click the equip button pressed.
If our selected ability that we have selected, if the currently selected ability has the status of
equipped.
So I can remember what that slot is.
And if we choose a different one, we can clear the previously selected slot.
So what I'm going to do is get that status.
I'm going to make a const F gameplay tag called status.
We can also call it selected status if we want to be more specific.
And this is going to be the status that we get from our ability system component.
We need to get the status for that ability.
Now, if we call, get or ask.
And type get status.
We only have get status from spec, so we have to get the ability spec first and then get the ability
tag.
Unless of course we create a convenient function that can get the status from the ability tag.
At this point, since I'm doing this for the second time, I believe I'm going to go to our ability
system component and make an easy to use function that can return the status.
So this function will return an gameplay tag.
It can't be static as we need to call get status from spec, which is a member variable.
So we're going to call this get status from ability tag and it's going to take a const gameplay tag
called ability tag as an input.
So let's generate this function.
And first I'm going to check to see if the const gameplay ability spec pointer that I get from the function
get spec from ability tag passing in ability tag is a null pointer.
If it's not null, then I can simply return get status from spec passing in spec.
But if it is a null pointer I have to return an f gameplay tag.
And of course spec has to be referenced if we pass it in there.
So now we have git status from ability tag.
Let's go back to spell menu widget controller and just call that.
Passing in the ability tag of our selected ability.
So selected ability.
Dot ability.
So now that our selected status is known.
I'm going to check that status.
So I'm going to say if selected status.
Dot matches tag exact and I'm going to check against for a gameplay tags.
Abilities, status equipped.
I want to know if it's equipped.
If it is, I'm going to save that slot as we have now a selected slot.
I'm going to set that here.
I'm going to say selected slot equals and we need to get that slot.
So we're going to use get or ask.
And the function we want to call is get input tag from spec.
But we don't have the spec, do we?
It'd be nice if we had get input tag from ability tag.
So let's make that function too.
So we'll go back to our ability system component.
We'll have one that returns a gameplay tag called get input tag from ability tag.
These are all good functions to have.
So let's generate this function definition.
And this one will be similar.
We're going to get our slot from the spec, right?
We can say if const F actually I'll just copy this.
It's the same thing almost.
We'll get the ability spec check it.
If it's not null, we're going to return our input tag from the spec.
Get input tag from spec passing in a dereferenced spec.
That's it.
So now we have get input tag from ability tag.
Just a convenience function.
So we're going to call that get input tag from ability tag passing in selected ability dot ability.
Okay, so now when we click the equip button, we're saving the selected slot or the input tag of our
currently selected ability if it's an equipped ability.
Otherwise it doesn't really have an input tag, right?
So that's important.
We'll keep track of that.
We also know that we're waiting for an equip selection.
So when our user actually makes a selection, we need to handle it.
When they click on one of the spell globes and the equipped spell row icon, we need to handle that.
So let's make a blueprint callable function for that.
We'll make it void and it's going to be called spell row globe pressed.
Now, when we press one of those globes in the equipped spell row, we need to know what the slot tag
is, what's the input tag for that particular globe.
And we need to know the ability type, whether it's offensive or passive.
So we need to gameplay tags.
So const gameplay tag reference, we'll call it slot tag and const gameplay tag reference and we'll
call it ability type.
We need to know both of those things.
So let's generate this definition.
Now, what are we going to do here?
Well, if we're not waiting for an equipped selection, we should return.
We don't really want to do anything, so we're going to say, if not, be waiting for equip selection,
simply return.
Now, another reason to return right here is if we're trying to assign an offensive ability to a passive
slot and vice versa.
We don't want to do that.
I'm actually going to make a comment here that says check selected ability against the slots ability
type and then in parentheses don't equip an offensive spell in a passive slot and vice versa.
We'll put this on two lines.
So how do we check that?
Well, we get the selected ability type, so const f gameplay tag.
Selected ability type and we get that from ability info.
We findability info for tag using our selected ability ability.
And we get the ability type from that and we check if selected ability type dot matches tag exact against
our ability type for the globe that we're pressing.
So this is true.
If we're pressing on a globe that's the same ability type as our selected ability type, that's okay.
What's not okay if they're not the same?
So we'll put a negation operator and return.
In that case, Actually, I don't really need this to be in a function body.
We'll just put it all on one line.
So we'll return in that case too.
If we make it past this point, then we have an ability selected from one of our trees.
We've pressed the equip button, so we're waiting for a selection.
And then we've pressed one of the globes in our equip spells row.
And at this point it's time to let the server know we're trying to do something important here.
We're trying to reassign an ability to a new slot or assign it for the first time.
This is important enough for the server to know, so the ability system component needs a server RPC
to equip an ability.
So at this point we go over to ability system component, the aura ability system component that is,
and we make a public function that's a server RPC.
We'll put it just under server spin spell point and this will be void server equip ability.
Now, this needs a couple of tags.
It needs a const gameplay tag reference called ability tag.
We need to know what the ability is and we need to know the slot.
So we need const gameplay tag ref.
Slot.
That input tag, right?
Now, this needs to be server and it needs to be reliable.
We don't do this lightly.
Let's generate the definition, the implementation version of this.
And here is where we need to make some checks.
So first of all, we need to check to make sure that the ability spec exists in our activatable abilities
for this ability tag.
Otherwise there's no point in continuing.
So we're going to say if f gameplay ability spec.
That's a pointer called ability spec.
We're setting it with git spec from ability tag passing in the ability tag.
If we don't make it this far, there's no point in doing anything else because we're trying to equip
an ability that's not in our activatable abilities.
If it's not at least eligible, in other words, locked.
It's not in our activatable abilities.
It's like it doesn't exist, and that's what we consider locked abilities.
Now, here's the thing.
We need to know this abilities input tag.
And we're assuming that this ability, if it's already equipped, is going to get a new input tag.
So the current input tag for this ability is the previous slot because we're about to change it.
So we're going to say const gameplay tag ref.
Previous slot.
I'll just say prev slot and we're going to use get input tag from spec.
We could get it from the tag too, but we're going to pass in the ability spec.
Dereferencing it.
Of course.
Now we also want the status.
We want to know what that status is.
So we're going to make a gameplay tag called status, and that's going to be from get status from spec
and we'll pass that spec right in.
Now that we know the previous slot, it's still the current slot, but it's about to be the previous
slot, right.
We want to know if that status is either equipped or unlocked.
If it's equipped or unlocked, then we can assign it to a new slot.
Otherwise, it's either locked, which we should never get here if that's the case or it's eligible
if it's eligible.
We've never spent any points on it.
We have no business trying to equip it.
So what we can do is say const bool B status valid.
And the only way the status is valid is if status is equal to and we need for a gameplay tags
get dot abilities equipped.
Or.
Status is abilities unlocked?
So we'll say or status equals abilities unlocked.
That's the only way we have a valid status here, if it's locked or if it's eligible.
We have no business trying to assign it to an input tag to a slot.
Now, once we have that status and we know it's valid, we can check if B status valid.
Now we need to do a few things.
For one, any abilities that have this slot are now going to be cleared out.
We need to get any abilities that have this particular input tag and clear them because we're pushing
that ability out of its slot in favor of this one.
We've just selected it.
Let's say we already have an ability in the left mouse button slot and then we selected another one
and now we're trying to equip it into the left mouse button slot.
Well, that ability that was in the left mouse button slot needs to be cleared.
So we need a function here that can do that.
So let's do that here in our ability system component.
We're going to have a new function.
A couple functions, actually.
For one, I want a function that can take an ability spec and remove its input tag and I'm going to
call that clear slot.
So I'm going to make a void function clear slot that takes an f gameplay ability spec.
By Pointer and we'll call it spec.
So I'm going to generate a definition for this.
Now, all this function is going to do is first get its slot.
So we're going to say F gameplay tag a const one called slot and that's going to be get input tag from
spec passing in our Dereferenced spec pointer.
So once we know that slot, then we can take the spec and get its dynamic ability tags and call remove
tag from it.
So we're going to remove the slot and as soon as we do that I want to force it to replicate.
So I'm going to mark the ability spec as dirty passing in the spec.
That's it.
That's all I want to do for clear slot.
Now I'd like a function that will clear any abilities we have of a given slot.
So for that we'll make a function called clear abilities.
Of slot.
This will take a const.
F game play tag.
Ref called slot.
So this is going to remove any input tags which are this particular slot from any of our gameplay abilities.
So we're going to loop over our activatable abilities, which means we need an F scoped ability list
lock called active scope lock.
Passing in a dereference this pointer and we're going to loop over all of the ability specs in our activatable
abilities.
So for gameplay ability spec, we'll make it reference called spec.
We'll use get activatable abilities and if that ability has this given slot I want to call clear slot
on it.
But how do we know if it has the slot?
Well, I need a function that can easily tell me if an ability has a slot.
So I'm going to make one more function that returns a bool called ability has slot and it's going to
take the spec for the ability.
We'll pass it in by pointer gameplay ability spec pointer called spec and a const gameplay tag called
slot and it'll return a boolean.
So for this function we'll return true if the given ability spec has the given slot and for that we
have to loop over its dynamic ability tags.
So for gameplay tag tag in spec dynamic ability tags if the tag dot matches tag.
Exact for the slot will return.
True.
Otherwise we'll return.
False.
So now we have ability has slot.
This can actually be a static function.
And in clear abilities of slot.
As we loop through all the specs and activatable abilities, we're going to check if ability has slot
passing in.
First of all the spec and we have to pass in the address of the spec and then the slot which we have
here.
And if the ability has the slot we're going to call clear slot on it, passing in the ability spec.
And again by address because this takes a pointer.
So clear abilities of slot loops through all the abilities.
If they have the given input tag the slot, we're going to clear that slot, just remove it from their
dynamic ability tags.
So after we made those helpful functions we can come back to server equip ability and if we have an
ability that's either equipped or unlocked and we're trying to assign it to a slot, what are we going
to do?
Well, first we're going to clear all abilities of that slot.
So we're going to call clear abilities of slot passing in that slot.
That's the first thing.
So let's actually make a comment and say remove this input tag in parentheses slot from any abilities.
We'll say any ability that has it because only one should have it.
Now.
In addition, we should also clear this abilities slot that we're trying to change, right?
Because we're about to change its slot to something else.
So I'm going to have another comment that says clear this ability.
Slot this abilities slot.
Just in case it's a different slot.
So we're going to call clear slot on this ability spec.
And now we'll say, now assign this slot to this ability or this ability to this slot, this ability
to this slot, or maybe the other way around, whatever.
We're going to take the ability spec, take its dynamic ability tags and add tag.
This slot.
Now there's something else we have to do if the status is unlocked.
So we're going to say if status dot matches tag exact and we need to check to see if it's unlocked.
So f or a gameplay tags get that ability status unlocked.
And at this point I'm going to have a gameplay tags variable.
So let's just do that right here.
Const f or gameplay tags.
Ref gameplay tags equals aura gameplay tags get and we'll use that.
And replace it where we're calling.
Get there and there.
And down here.
Okay.
So if the status is unlocked, well, now we're assigning it to a slot so it's no longer going to be
unlocked.
We need to remove the unlocked tag and add the equipped tag.
It's now equipped.
We've promoted it.
So we're going to take ability spec take its dynamic ability tags, call remove tag.
We want to remove the unlocked tag.
I just have it right here.
And we want to add so ability spec dynamic ability tags, dot add tag.
We want to add the equipped tag.
Gameplay tags dot ability status equipped.
Now when we called clear slot we marked the ability spec as dirty.
Well, I'm going to actually do that up here with our ability spec.
I want to mark it dirty here, but I'll leave it in clear slot.
It doesn't matter if we call it twice, but just in case we clear it elsewhere.
I just want to make it very clear that we're marking it dirty here.
It won't make it replicate twice.
Now we've just made major changes to this ability spec and we need our spell menu widget controller
to know about it.
We need to make a broadcast about our ability being equipped and this is done on the server here, which
means we need a client RPC to call to get that information down to clients.
So we're going to make a client RPC as well.
So in addition to server equip ability, we're going to have a void function, client equip ability.
And client equip ability is going to want to send the gameplay tag.
Also send the new status.
Also the new slot and the previous slot so that we can clear that out if we need to.
So we're going to have several inputs.
Const gameplay tag ref called ability tag.
A const gameplay tag ref called status.
A const gameplay tag for the slot as well and a const gameplay tag.
For the previous slot.
So for gameplay tags, the ability tag its status, its new slot and its old slot.
So let's generate this definition here.
And we need to call this function.
Right here.
So we're going to call it just outside this if statement, client equip ability, and we're going to
pass the ability tag.
We also know for sure that it's equipped now.
So we're going to say gameplay tags, dot abilities.
Status equipped.
We need to pass that slot and we need to pass the previous slot which we cashed off before going and
changing it.
And client equip ability simply needs to let our widget controller know.
So for that reason we're going to have a delegate to broadcast all four gameplay tags.
So let's make that.
So we're going to declare a multicast delegate.
With four params.
We'll call this f ability.
Equipped.
And we've been using the Convention on the ability system component to not put signature at the end.
So we'll just keep up with that and we need const gameplay tag input parameters.
And right here the first one can be the ability tag.
Now the second one is going to be the status.
The third one is going to be the slot.
The fourth one will be the previous slot.
So we'll say previous slot.
And that's it.
So we need to make one of these.
We'll put it down here next to our other delegates.
And this will be ability equipped and we'll broadcast this in client ability equipped.
Here it is.
So ability equipped broadcast and we'll broadcast ability tag status.
Slot and previous slot.
And now our widget controller just needs to subscribe to this.
So let's make a callback function and spell menu.
Widget controller.
We could do a lambda, but trust me, we're going to need a function.
So let's make one that takes four input parameters, all gameplay tags.
So here in spell menu widget controller, we're going to have an on ability.
Equipped and it'll take four input params.
We'll have the ability tag.
We'll have the status.
We'll have the slot and the previous slot.
We'll call it prev slot.
We'll call it previous slot.
So we'll generate this definition.
And first things first, we're going to subscribe to the delegate and bind this to it.
So all the way up at our bind callbacks to dependencies when we have our ability system component right
under our first lambda, we're going to say get aura, ask.
Ability equipped.
And we're going to add you object.
With this as the user object and at you spell menu widget controller on ability equipped.
And now we can flesh out on ability equipped and we're almost done.
So the first thing on ability equipped can do is say, well, we're no longer waiting for equip selection.
That's going to be false.
Now the next thing we need to do is broadcast empty info.
If the previous slot is a valid slot.
So this is only if we're equipping an already equipped spell into a new slot.
So what I'm going to do is make an or a ability info struct called last slot info and it's going to
be just an empty f or a ability info and we're going to take last slot info.
Dot status tag and we'll set it to for gameplay tags.
Get.
Dot abilities status unlocked.
We'll take last slot info.
Dot input tag and set it equal to the previous slot.
And we're going to take its ability tag.
So last slot info, dot ability tag and we'll make this abilities none.
So it's not really occupied with anything substantial.
Now I'm also going to make a const for a gameplay tags.
So we'll use that here.
And so what we're going to say here is.
Broadcast empty info.
If previous slot is a valid slot only if equipping an already equipped spell.
So we'll take ability info delegate.
We'll broadcast last slot info and we'll just pass in abilities.
None.
Oh, sorry.
And that's it.
Now, next, we're going to create another ability info to broadcast, and that's for the current slot.
So we're going to say for ability info info and this will be from our ability info and we're actually
going to find ability info for tag using our ability tag.
So this will be the actual one.
We're going to get the ability status.
So status tag, we're going to set that to the status because that has changed.
We're going to take its input tag that has also changed to the new slot and we're going to take ability
info, delegate and broadcast info.
So clear out the old slot and fill in the new slot.
Now, in addition to this, we're also going to want to stop playing those animations from our equipped
spells row.
So in other words, we want that stop waiting for equip delegate broadcast.
So we'll broadcast that.
And this takes in the ability type.
So we need to get our ability info.
We need to find ability info for tag, pass in the ability tag and get the ability type.
And then we can stop playing those animations.
Okay, so that's a lot.
But we can just quickly come full circle here and see the full cycle of what's going on.
The first thing is equip button gets pressed right.
We save our selected slot.
If it's an equipped ability.
Now we're waiting at this point and when the spell row globe is pressed, we need to tell the aura ability
system component to execute server equip ability.
So we're going to do that here.
We're going to say get aura, ask server, equip ability passing in our ability tag and that's our selected
ability tag.
So selected ability, dot ability as well as the slot tag.
And our server is going to do all kinds of stuff, right?
It's going to first make sure that that ability has it equipped or unlocked status.
If that's true, it's going to clear all abilities.
Really only 1 or 0 will have this slot.
But if any abilities that we have have this slot, we're going to clear that.
We're also going to clear the slot of the ability that we sent up and then take that ability and add
the slot that we're now trying to occupy.
And if it has the unlocked status, we're going to remove that status and add the equipped status and
mark the ability spec dirty and call client equip ability.
Now that's going to just broadcast the ability tag, the status, the slot and the previous slot and
our spell menu widget controller is going to respond to it with an ability equipped.
It's then going to say we're no longer waiting for an equipped selection.
It's going to broadcast info for the previous slot.
Basically an empty gameplay ability.
Abilities.
None.
Right.
And then it's going to broadcast the current slot and then stop those animations on the equip spells
row.
So with all that, we made a lot of changes.
We now just need to compile launch the editor and see what happens.
Okay, I'm going to click open just because I can, but I'm going to come straight in and play test,
click on spells, click on this ability, click on equip and select right mouse button.
Okay, so of course, I forgot something.
Our globes here in the equip spells row.
These need to call our blueprint callable function.
What's that function called?
It is spell row globe pressed.
We need to call that passing in a slot tag and an ability type.
So a couple things.
I kind of jumped the gun here in our equip spells row.
First of all, our globes, they all need to know what their ability type is in addition to their slots.
And you know what?
If I put these images above the box root, then I can still select these globes.
Okay.
That's what I wanted.
So we have these globes, right?
We need them to know what their ability type is.
And I don't think they have an ability type gameplay tag.
So we need to go to the equipped row button.
And the equipped row button needs to know what its ability type is, not just its input tag now.
So we're going to add a new variable called ability type.
It's going to be a gameplay tag, obviously.
We can compile, we can check the I icon and back in equip spell row.
We can set all of these six to have the ability type of offensive.
So abilities type offensive and then these two can be passive abilities type passive.
So now they know what their ability type is.
They need to know because they're going to call spell row globe pressed passing in their slot and their
ability type.
So let's do that in Onclicked.
So for the equipped row button, we need to take that button.
Ring check is variable, come back to the graph and as soon as our widget controller is set.
I'm going to assign the delegate there.
So what I'm going to do is take my button ring.
Assigned to Onclicked.
We'll hook that up.
And we'll get our spell menu Widget Controller.
And call.
Spell row globe pressed and don't know if it's a blueprint callable it's not.
So we'll make that blueprint callable and we'll have to restart.
So closing the editor and restarting.
All right, let's get back into the equipped row.
We'll get our widget controller and call spell row globe pressed.
As soon as we've clicked.
And we need to pass in two things.
The slot tag, that's going to be the input tag here.
And the ability type.
We need to pass that in.
And then that'll start the whole chain.
And we can test that.
So selecting an icon.
Clicking on equip choosing B.
Now, here's the thing.
The reason we're seeing this is because all of these that don't have valid ability tags, their tags
are set to abilities.
None.
And that's what we're broadcasting.
So we don't want there to be a match.
In other words, we don't want those abilities to have their ability tags set to abilities, none.
That's going to result in what we see here.
But to avoid that match, we're going to set their ability tag to literally nothing.
We're going to uncheck that and set them to an unset ability tag.
So just doing that in the offensive spell tree, we should be able to see those tags not change.
We'll do that to the passive spell tree as well.
So we'll go back to the passive spell tree, select these three ability globes and set their ability
tag to nothing.
And that's going to save them from being set to an ugly white image.
Now for the equipped spells row, what we're broadcasting.
In our equip row button.
For our ability info delegate.
We're checking against the input tag and for that empty slot info.
The input tag is the previous slot.
That's why we're only seeing that white on the slot.
That was the previous slot.
Right.
If it starts off as the left mouse button, then we're only seeing that white there on the left mouse
button.
So we're getting a match here when we check the input tag.
But what we should be broadcasting up in this ability info here is an ability info that contains textures
for an empty globe.
Right?
We want the icon and we want the background both to be transparent.
Now what we can do is because we're broadcasting abilities, none is we can check if ability tag is
abilities none.
So we can say matches tag exact abilities dot none.
And if so, we'll clear the globe.
So let's do that.
And if it's not abilities, none will continue.
And here's our clear globe.
Which what it does is sets those two to a transparent brush.
So no problem.
So let's see how that works.
All right.
Cool.
That looks like it's working now.
Everything is working except for our overlay widget controller.
I can right click and I can use this ability on the right mouse button.
I can also assign it to one through four if I choose four, for example.
Now I'm using the four key, so that's great.
But we also have to update our overlay.
So as soon as this has been broadcast, we also need to respond on our overlay the same way we did in
our widget controller.
And that's something that I think we can do in the next video.
For now, I think this is enough and really this is quite a lot.
And before we actually wrap up, what we can do is we can unlock this ability and see if we can equip
it to the right mouse button.
And look, we can.
Oh, and we cleared out the left mouse button.
Only in the HUD, though.
But I can right click and we see electrocute activated.
Cool.
So a couple of things to fix.
Notice that we're clearing out everything in our equipped row.
When we equip something.
So that's not exactly what we want, right?
So we got a couple of things to fix in the next video.
I'll see you then.