Welcome back.
So our spell menu is coming along, but we still have a few things to change.
Now I'm going to take my nav mesh bounds volume and move it forward so my enemies can't come at me while
I'm testing as it's a little distracting when they're swinging their spears, thrusting them at me while
I'm trying to test something in the HUD.
So I'm going to go ahead and just do that little fix so even I can attack them, but they can't come
to me and that's what I want.
Okay.
So let's talk about what we want to do in this video.
We want to handle these two buttons.
At least we're going to handle enabling and disabling them.
And that's going to depend on a couple factors.
So the first thing is, when I open my spell menu, I'd like them both disabled by default.
That's because if I don't have a spell selected, neither one should be enabled.
I shouldn't be able to spend a point unless I have a spell selected now.
As soon as I click and select one, then we should see some changes depending on the scenario.
Now I just clicked on my firebolt spell which is currently equipped now.
If I have an equipped spell that I've selected, I'd like to be able to spend a point on that equipped
spell and that would allow me to upgrade that spell to the next ability level.
So when I click on an equipped spell, this button should be enabled on top of that, I'd like to be
able to reassign that spell to a different input.
So my equip button should also be enabled.
When I select an equipped spell.
If I click on equip, then I want to be able to reassign it.
So both should be enabled when I click on an equipped spell.
Now, when I click on a spell that's locked, or at least a spell globe that has the locked icon showing,
then spend point should be disabled.
We shouldn't be able to spend a point on a locked spell and so should equip, equip should be disabled
as well.
So those are the probably the two easiest cases for whether or not we should enable or disable the buttons.
But we have other scenarios too.
For example, if I level up.
Then all of a sudden I have a new type of icon.
This spell is actually in the eligible status.
It's not equipped.
It's actually not even unlocked yet.
We can't equip it.
So a eligible spell should not have the equipped button enabled.
It should still be disabled, but we should be able to now spend a point on it to unlock it.
So in that case, spend point should be enabled.
Right now there's another scenario that we can't really test yet, and that's for an unlocked spell.
An unlocked spell is going to appear just like an equipped spell.
It's going to be colorful, not grayed out for the background.
But even though it's not equipped, we should be able to still spend points on it to upgrade it and
we should be able to equip it because it's been unlocked.
So that is the scenario for an unlocked spell.
We should have equip and spend point buttons enabled for an unlocked spell so we can upgrade it and
or equip it.
So unlocked and equipped should have both buttons enabled.
Locked means no buttons enabled equipped means both buttons enabled unlocked.
Also both buttons enabled and eligible means.
We should be able to spend a point on it but not equip it.
So I'm going to give you a quest to handle enabling and disabling the buttons.
But first we're going to make sure that those buttons are disabled by default when the spell menu is
first opened.
That's something we can do in Pre-construct because those buttons can simply be disabled by default.
I'm going to type Pre-construct and get an event Pre-construct out and get those buttons.
We have the spend point button.
We can now check is variable for that and rename it from wide button to spend point button.
Let's do that.
Or we'll call it actually button underscore spin point and we'll take the other button, the equip button,
and we'll rename this one to button, underscore, equip.
And we'll check is variable for that.
So they're both variables now and back in event pre construct we can get button spend point we can get
button equip and we can call set is enabled.
So we'll set both of these to disabled.
An event pre construct.
And that way whenever we open our menu, we know that we won't have any globes selected, right?
So we're just going to disable both of them.
So they should be disabled by default.
And as soon as we open up our spell menu, we see they're both disabled.
That's what I want.
It's only when we start to select things that we change our enabled status on these buttons for an equipped
one.
Again, let's reiterate selecting an equipped spell globe means both of them should be enabled.
Selecting a locked spell globe means both of them should be disabled.
For an eligible spell if I had leveled up here.
We have an eligible spell globe.
When we select this an eligible spell globe cannot be equipped, but it can be unlocked.
So spin point only should be enabled.
And then for a spell globe that's unlocked, which we can't test right now.
They should both be enabled as well.
So I'm going to give you a hint for this quest.
The end result here is that your spell menu should have a delegate to respond to, and that delegate
should broadcast two booleans, one Boolean for setting our spend button point enabled and one boolean
for our button equip enabled.
We need to know those pieces of information and when we click on a spell globe, when it becomes clicked,
we can call a blueprint callable function for when a spell globe is clicked.
So we'll need to add a blueprint callable function in our widget controller that can then make the determination
based on that particular spells status.
Right.
So we're going to need to check the status.
But there's another consideration you have to make as well.
And that's how many spell points we have, right?
If spell points is ever zero, should we ever enable spin point?
No, the button should be grayed out.
Then too.
So take spell points into consideration.
So we have two things to consider the abilities status and our spell points count.
So this is a bigger quest.
This is going to be a larger, more challenging quest.
There are more pieces to it.
So if you're up to the challenge, I'm going to give you that quest now.
So here's your quest.
I'd like you to enable and disable our spell menu buttons.
Now, that means you have to take some information into account, right?
Obviously, you're going to need to account for the spells status, so you need to check that in our
widget controller.
Now another thing you need to account for are the spell points.
You should never enable the spell points button if spell points is zero.
So that's another thing to take into account.
So this is a bigger quest and I'm giving you a lot less guidance here because this is a good opportunity
to try a little bit more of a challenging quest with minimal guidance and just see how much your capabilities
have evolved.
So give it a try.
Pause the video and conquer this quest now.
Okay.
So there are kind of a lot of parts to this.
Quest Right.
But we're going to break this down step by step.
And this is hopefully how you did it, just one step at a time.
If you don't know all of the solutions to all the challenges and aspects to a quest, just take it one
step at a time.
Now, I like to go sequentially in order.
What's the first thing that happens right when we open the spell menu?
Well, we're already disabling those two buttons, so that's the first step.
The next step is what happens when we click on a spell globe when we select one.
Well, I want my spell menu widget controller to know about it.
So we need a blueprint callable function, but the spell menu widget controller also needs to know which
ability is being selected so it can check the status of that ability.
So when we click on one of our globes, we need to inform the spell menu widget controller of which
ability is being selected and we identify those abilities by their ability tag.
So we're going to create a blueprint callable function for when we click or select a spell globe.
So I'm going to go into my spell menu widget controller.
I'm going to create a brand new blueprint callable function here.
So I'm going to call it void spell globe selected.
And it's going to take a const f gameplay tag by reference called ability tag so we know which ability
has been selected and it's going to be blueprint callable.
So we'll give it a new function with blueprint callable.
Let's make sure we have the gameplay tag container header file included.
So gameplay tag containers there.
And now we have a blueprint callable function.
I'll generate the definition of it like that.
Now, when a spell globe is selected, we're going to do some stuff here.
But before we get to that, I don't want to get too far ahead of myself.
I'm going to make sure that I call my blueprint callable function when we click on one of our spell
globes.
So closing the editor, clicking the debug button so we can compile and launch again.
I'm going to go back and make sure we call this function.
So back here in the editor, I'm going to go into my spell globe button and make sure that whenever
we click on one of these that we also call our blueprint callable function on the spell menu widget
controller.
And because we have a BP spell menu widget controller that we're setting, as soon as our widget controller
has been set, we should be able to access that blueprint callable function from it.
So I'm going to get my BP spell menu widget controller and call its blueprint callable function spell
globe selected.
So here's Spell Globe selected, we're going to call it, and it requires an ability tag.
And each of our spell globes has its own ability tag.
So we're going to take that ability tag and we're going to pass it on in.
And now we know in C plus plus we can handle what happens when a spell globe is selected.
So that's that step taken care of.
Now, the ultimate end goal here is for our spell menu to receive a delegate that tells it two things
two Boolean values for setting the enabled status for these two buttons.
So we need a delegate to broadcast to Booleans.
That's our next step, or at least the step I'm going to do next.
I'm going to declare a dynamic multicast delegate that can broadcast two booleans.
So we'll declare it right here and spell menu.
Widget controller.
We'll say declare dynamic.
I'll just let Autocomplete finish this one because the only thing I need to change is I should have
two params, so I'll use two params like that.
So what are we going to call this?
Well, it's going to be when a spell globe is selected, so I'm going to call this F spell globe.
Selected signature.
And it's going to broadcast to Booleans.
And for the dynamic multicast delegates, we can say bool, comma and the name of the bool.
So the first one will be for enabling the spend points button so we can say be spend points button enabled
and a bool called be equip button enabled.
So we have this dynamic multicast delegate that can broadcast to booleans whether or not we want to
enable the spend points button and the equip button.
So we need to create one of these delegates as a blueprint.
Assignable delegate and I'm going to call this spell globe selected delegate.
And we'll make it blueprint assignable.
So now we have that delegate.
We just need to figure out when to broadcast it and with which values to broadcast it.
So we know that Spell Globe Selected will be called when we click on one of our spell globes and we
have the ability tag.
Now when a spell globe is selected, we need a couple of pieces of information we need first of all,
the spell points.
How many spell points do we have?
And we can get that from our player state with Get Aura.
PS and the player state has get spell points, so that one was pretty easy.
We can store that in a const int 32.
So const int 32 spell points.
And there's that information.
Now, we also need the status of our ability, and we need to be careful here because this ability tag
may or may not be set on the globe that we're clicking on.
Now, just to have a specific tag for when a globe has no ability assigned to it, we should probably
have a specific tag for that.
An abilities.
None of sorts.
So if we go to or a gameplay tags, we can make a new ability and this can be the sort of null pointer
of ability tags we can call it abilities none.
So we can create this game play ability tag and it's going to just be abilities none designated for
no ability.
So it's not an unset tag.
So we're going to call this abilities none.
The name will be abilities none and we'll say no ability space like the null pointer for ability tags.
It's not a pointer, but it's like a null pointer.
It's specifically no ability.
Right.
So we can go in and set all of those globes that don't have abilities assigned to them to have abilities.
None.
So just kind of a convenience there.
You didn't have to do that, but I'm going to do it.
So how are we going to handle this situation?
Because if we want to check the status of our ability based on its tag.
Well, we do have some functions on our ability system component.
If we go to our ability system component.
We have get status from spec and we have get spec from ability tag, but get spec from ability tag returns
a null pointer if we don't have that specific ability with that tag.
So we have to take that into account here.
If we're going to get spec from ability tag, we need to check that pointer because if it's null, we
don't have an ability with that tag or we use the tag.
That's not valid.
It could be abilities none or even not set at all.
So what we can do is place an If check here.
We can say if get aura ask.
And we can call get spec from ability tag passing in the ability tag.
Now if this is a null pointer, in other words, equals null pointer here.
Then what we're dealing with is either an ability tag for an ability that we don't have yet or an ability
tag for an ability that doesn't exist.
It could be abilities none, for instance.
So in this case, we need to know what to do with our buttons in the spell menu.
So let's think about all the scenarios that can happen.
The first is that the ability tag is not a valid tag, right?
So in other words, we can say bool be tag valid equals ability tag dot is valid.
We can also say that the ability tag is none.
We can say bool be tag none equals ability tag dot matches tag and we could use f or a gameplay tags
get dot abilities.
None.
Right?
I'm not necessarily going to use these booleans.
I'm just sorting out our thoughts here so we know if the tag is valid.
We know if the tag is none and then we know if we get the spec from the ability tag and it's a null
pointer, we know that in that case we don't have that ability in our activatable abilities.
Now, does that necessarily mean that that ability is locked?
Well, if the ability is eligible, we know that that ability exists on our ability system component.
It is an activatable abilities.
It's just that its status is eligible.
Right?
It's not locked.
It's just eligible.
So if it's not in activatable abilities, in other words, if this is null, then the ability is locked
or this tag doesn't correspond to a valid ability, in which case we can just treat this as if the ability
icon is locked.
So we can say here bool be spec valid and we can say that when we call get spec from ability tag.
If it's not a null pointer, then we have a valid spec too.
So what I'm going to do is I'm going to create a local gameplay tag called ability status.
And I'm going to set that status to locked in a number of scenarios.
One, if the tag is not valid, so I'm going to say not be tag valid.
But I'm going to have an oar here.
And if the tag is none, if it's ability is none, I want to set that status to locked.
So I'm going to say B tag none.
I'm also going to have another or for if the spec is not valid.
So I'm going to say not B spec valid.
In all those cases, I'm going to set ability status equal to abilities none.
So I'm going to go ahead and just create a variable for for a gameplay tags at the top here since I'm
going to reuse it.
So I'm going to say const for gameplay tags.
Gameplay tags equals for a gameplay tags get and I'll use gameplay tags down here.
And I'll also use it where I'm setting ability status to gameplay tags dot and it's going to be abilities
status locked.
So if the tag isn't valid or if it's abilities none or if the spec is not valid, I'm going to treat
this like a locked ability and these can all be constable's.
So that's what I'll do there.
But otherwise we'll have an else here because if the tag is valid and it's not abilities, none.
And the spec here that we get from the ability tag is also not a null pointer.
Then I want to get the status from the ability spec.
So if we don't want to call get spec from ability tag twice, which would be wasteful because it involves
a for loop, we could just create a pointer of type gameplay ability spec here we'll say f gameplay
ability spec, we'll make this pointer called ability spec and we'll set it with get or ask get spec
from ability tag here and then we'll check this pointer right here.
And then we have the spec.
We don't have to call this function twice, and then here we're going to say ability status equals and
we can get our ASC and we can call get status from spec passing in the ability spec which needs to be
dereferenced.
So we'll pass in the dereferenced ability spec right there.
And we know that this is not a null pointer because be spec valid has already been set.
Ability spec is not null pointer if be spec valid is true.
So now we have the ability status as well.
So based on spell points and ability status, we need to set the value of these booleans.
So it'd be handy to have a function that can take in the spell points, the ability status and then
set to booleans.
And we could just pass two booleans in by reference non-const and the function can set them for us.
So I'm going to make a function that can take in two booleans and fill them in.
So let's do it.
I'm going to go back into the widget controller.
This can be a private function.
It's just internal to the widget controller.
It's going to be void and it's going to be we'll call this should enable buttons and this can take in
a const gameplay tag for the status.
We'll pass it in by reference, we'll call it ability status and it'll take in an int 32 called spell
points and two bools by non-const reference.
So a bool reference called B should enable spell points button and b should enable equip button.
And this is also a bull.
Cool.
So we have should enable buttons.
We'll pass in the status the spell points and two bools by non-const reference.
This one should be a reference to that should be filled in by should enable buttons.
Let's define this function.
And we'll just pass into bulls here.
So how is this function going to behave?
Well, we need to check the status.
I'm going to go ahead and copy this line where we're getting gameplay tags and I'm going to check ability
status.
I'm going to say if ability status and I'm just going to use matches, exact matches, tag exact and
match against gameplay tags, dot abilities status and we have the four statuses.
We'll just have an if for each one.
So we'll say equipped first and we'll do an else if.
And we'll check the same tag.
Only we'll see if it's equal to abilities, status and we'll do eligible next and the other two cases
as well.
So we'll do else if and we'll check against the status unlocked and finally we'll do.
A check against status.
Locked.
Now, based on the status we know if we should enable the equip button.
The equip button depends solely on the status, not on the spell points.
And if our ability is equipped already, then the equip button can be enabled.
If it's eligible, it should not be enabled because we haven't unlocked it yet.
If it's unlocked, it should be enabled and if it's locked, it should not.
So we'll just specifically say that the equip button will be enabled.
So we'll set it to true if equipped.
If it's unlocked, we can enable the equip button.
But if it's locked or eligible, it should be false.
So we'll paste in false here for eligible and false for locked.
Now for the spend points button, we have to check both the status and spell points.
Right.
If the abilities status is equipped, then we should enable the spend points button or the spell points
button if we have spell points.
That's why we pass spell points in.
So we'll say if spell points is greater than zero.
In that case the should enable spell points button should be set to true.
And the same goes for eligible.
If we're eligible, we should be able to spend points on it.
If we have spell points.
So we should set the spell points button to enabled.
Then if we have points and if it's unlocked, we should be able to spend points on it.
So we'll say if spell points is greater than zero.
So we'll set it to true then.
But if it's locked, it should be false.
So really we should set it to false first and only set it to true in those circumstances.
So we'll say should enable spell points button equals false first and then we don't have to set it to
false.
We'll only turn it to true in these three cases.
And if you wanted to, you could set both of them to false up here and only flip them to true in the
case that we're changing them to true.
In other words, we could take should enable equip button equals false and do that up here at the top
and then only set them to true and never have to worry about setting them to false because that's their
default state.
That would make this last else if redundant and we could remove it so we could do it that way too.
Now should enable buttons only changes things that we pass into it.
In other words, it doesn't change any state on the widget controller, so it could be a static function.
So we're going to go ahead and make it static.
So now we have should enable buttons and we can determine those boolean values.
Here in spell globe selected, we have to create two local booleans.
One will be be enable spend points.
We'll set it to false by default and bool be enable equip.
We'll set it to false by default.
We're setting them to false by default in this function and then flipping them to true.
So it's a little redundant, but I don't want this static function to just assume that they're coming
in false.
So these two lines will stay and these two lines will stay as well.
So we're going to call should enable buttons passing in the ability status.
So ability status, passing in spell points and the order matters.
Here we have to do the enable spell points button first.
That should say spend points, but that's okay.
We're going to say enable spend points goes in first and then enable.
Equip goes in second.
And after calling this function, these booleans will have the correct values, at which point we can
just broadcast our delegate, which is called spell globe selected delegate.
So let's do it.
Let's broadcast that delegate we'll call broadcast broadcasting and the order matters here to spend
points button enabled is first.
So we're going to say enable spend points goes in first and enable.
Equip goes in second.
So at this point, we're now going to broadcast two Boolean values up to our widget, which will handle
them.
And of course this pointer for the ability spec could also be const.
So we'll do that too.
And that should take care of the code side.
Let's hop back over to Blueprint.
So back in the editor, we can go back into spell menu.
And I can also get my spell globe.
Spell glow button widget here open just to make sure that Onclicked is calling spell globe selected.
Now the first thing I'm going to do is see if those values are correct.
So I'm going to place a breakpoint here just before broadcasting this delegate, before we even assign
anything to it.
And I'm going to see if it broadcasts the correct values.
I'm going to click on an equipped button and see that both of these are true.
So that's correct.
I'm also going to click on one of these that doesn't have a valid ability tag and see what happens there.
And in that case, both Booleans are false and our ability spec is null in that case.
So that's also the correct scenario.
Now I would like to level up.
Now I have one of these that's in the eligible state, and if I click on that well, enable span points
should be true because I have spell points, but enable equip should be false.
So that's also correct.
So it's looking like our booleans are correct.
We can now handle this delegate broadcast in the spell menu and we need to do that after setting our
widget controller variable so we can do it right after we do that.
Let's move these sections down and add a new pin and I'm going to move these three wires down.
I'm going to hold control and click and just move these wires over.
And for this one, I'm going to get my spell menu, widget controller, get that and assign.
And I'm forgetting what the delegate is called.
It's called Spell globe selected Delegate.
And I'm going to remove that breakpoint.
So I'm going to search for spell globe selected delegate.
We're going to assign an event to that.
Hook this up to the second case in my sequence, and we now have two booleans that we can use to set
the enabled state on our buttons.
We have button equip.
And we have button spend point.
We're going to get the spend point button first.
We've been doing that first for everything here we're going to call set is enabled.
And we're going to pass in spend points button enabled to that.
We'll hook that up.
And for button equip we'll call the same function, but we'll hook up, button, equip or equip button
enabled to that one and hook that up.
And this can be collapsed to a nice function called set buttons enabled.
So I'm going to collapse the function called set buttons enabled.
So that's nice and clean.
We can give this a comment and say enable buttons based on ability, status and spell points.
And because we're disabling both of the buttons at the very beginning up here in pre construct, we
can now call set buttons enabled instead.
And these nodes can be replaced by one neat node will pass in false for both.
And we have is in enabled one and two.
So these input parameters should actually be called better names.
So let's just rename those inputs to be nice and clean.
This one will be the spin point.
So we'll say spend points enabled and this one will be equipped.
So we'll say equip enabled like that, we'll compile it and let's see if that broke any of our nodes
and we see that they're looking fine.
Okay, so now we just need to test everything out.
Let's go ahead and open things.
And there we go.
We're enabling both buttons.
When we click on an equipped ability, we're disabling both buttons.
When we click on a locked ability or an ability globe that doesn't have a valid tag.
Now let's level up and get an eligible spell globe.
There's one.
And if we click on it, we can spend a point, but we can't equip it.
And the only other scenario is when we have one unlocked but not equipped, and we can't really test
that yet until we continue implementing more features in our spell menu.
But everything is updating as it should.
So things are looking really nice.
We now have our buttons enabled and disabled.
Depending on the status and the number of spell points we have.
The only other thing we can test is having zero spell points to start out with so we can set our starting
spell points to zero.
And also looks like we're accessing none.
Trying to read BP spell menu widget controller in spell globe selected in our spell globe button.
So let's go to that and see what's going on.
So right here, our spell menu widget controller is null.
So let's see if we can reproduce that error.
Okay.
It was when I clicked on the passive spell menu.
One of the globes in the passive.
So I think I might have not set my passive widget controller for the passive spell menu.
Let's see if we did not do that.
Okay.
We are setting the passive spell trees widget controller.
But let's go to the passive spell tree.
And make sure we're setting the widget controllers for its three globes.
We may not have done that yet.
I don't think I have.
So we do need to make sure we're doing that for the passive spell tree.
So we're going to get event widget controller set.
Sure enough, we haven't yet, so we're going to get our glow buttons and call set widget controller.
And we'll do it for all three of them.
And we'll set them to widget controller.
So we'll get our widget controller variable and we'll set them there.
And I'm going to collapse this to a function.
Set widget controllers.
So now that our passive tree is doing that.
Then we shouldn't get that error.
And that takes care of it.
So one of those subtle things that's easy to forget.
We'll put that up there at the top.
Okay, so things are looking nice.
We now have the spell menu buttons either enabled or disabled, depending on the status and or our number
of spell points, which, by the way, I just said we can test out starting with zero spell point.
So let's just test that really quickly.
I'm going to go to the public folder into player and Ora player state and start us off with zero spell
points and we'll just play test with zero spell points as well and see if the buttons update when we
level up in that case.
And I'm all ready for seeing an edge case that I haven't taken into account.
And that's leveling up with the spell menu open as things are going to change in that case.
So I'm going to open everything back up.
I'm going to press play.
I see that I have zero spell points and if I click on an equipped ability, the spin point button remains
grayed out, which is exactly what I want.
And the equip button is the only thing that should change.
And if I level up.
Then I can click on.
Either one of them and the spend point button becomes enabled and that's great.
Now, I wanted to test when we level up with the menu open.
I think that's an edge case.
That could be an issue.
So what I'm going to do is.
Go into my actually my overlay is where I'm doing it.
I'm going to go into the overlay.
Where we're setting input mode only.
I'm going to disable that so I can still move with the menu open and then I'm going to level up.
Now, I haven't had anything selected, which means these should both still be grayed out.
And of course they update when I click them, so that's fine.
But what if I select one and then level up?
Then nothing really updates until I select it again, which is okay because we shouldn't be leveling
up with the menu open anyway.
But it's possible that we do.
It's possible that we have some fireballs flying this way and then we open it.
And somehow have one selected and then we level up.
It is possible, right?
So we really should be broadcasting this delegate for whichever ability we currently have selected when
we level up, when spell points changes or our ability statuses change.
So that's something we should take care of and I think we'll handle that in the next video.
So I'll go ahead and just leave my node disconnected here for input mode only and we'll handle that
edge case in the next video.
So great job.
I'm proud of you for taking on this big quest, and I'll see you in the next video.