WEBVTT

00:06.800 --> 00:08.060
Welcome back.

00:08.240 --> 00:15.700
Now that our spell has cooled down, implemented, it's time to be able to visualize that cooldown.

00:15.710 --> 00:23.330
So as soon as I start to cast spells, my spells are going to be committed at one point or another.

00:23.330 --> 00:30.650
And as soon as that spell has its cooldown committed, I'd like to show something down here for its

00:30.650 --> 00:32.600
spell Globe icon.

00:32.600 --> 00:39.710
I'd like it to get dark so we know that it's not possible to cast that spell, as well as show the cooldown.

00:39.710 --> 00:41.540
How much time is left.

00:41.570 --> 00:48.170
Now there are ways to query your active gameplay effects to see how much time is left on their cooldown.

00:48.170 --> 00:57.170
So we'll be looking at that, but we're going to need a way to query that from within our Globe widget

00:57.170 --> 00:57.740
blueprint.

00:57.740 --> 01:05.450
In other words, if we go into UI and into spell globes, here's our spell Globe widget blueprint.

01:05.480 --> 01:14.310
We're going to need to to be able to respond somehow to our cooldown here so we can set properties from

01:14.310 --> 01:15.660
within our event graph.

01:15.690 --> 01:23.730
Now the way that I'd like to handle this is I'd like to have a special node, an async node, a task

01:23.730 --> 01:32.730
of sorts that allows me to do something when our cooldown begins and do something else when our cooldown

01:32.730 --> 01:33.390
ends.

01:33.390 --> 01:43.860
And if that task, that node that I'd like to create is capable of returning the cooldown duration at

01:43.860 --> 01:50.520
the time that a cooldown begins, then we can handle updating our cooldown time.

01:50.520 --> 01:57.060
The numbers that we show here from within our spell globe widget blueprint itself as there's really

01:57.060 --> 02:03.150
no need to query a spell or abilities cooldown over and over.

02:03.180 --> 02:03.660
Right?

02:03.660 --> 02:10.380
All we need to do is know when the cooldown begins, how much cooldown time there is and when the cooldown

02:10.380 --> 02:11.250
ends.

02:11.250 --> 02:15.900
And we can handle updating a sort of countdown timer ourselves.

02:15.930 --> 02:18.660
You see, we have to think about performance, right?

02:18.690 --> 02:25.350
We don't have to query our ability system component every single frame during this update.

02:25.350 --> 02:30.770
So in order to make one of those nodes, I'd like to make an async task.

02:30.780 --> 02:36.510
Now we've seen this kind of thing in gameplay abilities and to remind ourselves we can go to ability

02:36.510 --> 02:44.580
system or abilities, fire Firebolt and open a firebolt and play montage and wait is an asynchronous

02:44.580 --> 02:49.110
node and wait gameplay event is an asynchronous node.

02:49.110 --> 02:53.730
And on top of that we made our own asynchronous node target data under mouse.

02:53.760 --> 02:57.690
It just happens that valid data is executed immediately.

02:57.690 --> 03:00.270
But for something like play montage and wait.

03:00.270 --> 03:04.320
We have multiple execution pins and we know how these work.

03:04.350 --> 03:11.250
On the Cplusplus side, these pins are delegates and once those delegates are broadcast, those execution

03:11.250 --> 03:12.960
pins are executed.

03:13.050 --> 03:20.310
So I'd like to make a node that lets us wait for a cooldown to begin and to end.

03:20.310 --> 03:27.030
Now we need to use this asynchronous node that we're going to create from within our widget blueprint,

03:27.060 --> 03:29.220
not from within an ability.

03:29.490 --> 03:37.950
So ability tasks are made for use inside of an ability, but other types of tasks.

03:37.950 --> 03:43.650
Blueprint async tasks are made to be used in any event graph.

03:43.680 --> 03:46.290
They don't have to be used inside of abilities.

03:46.290 --> 03:50.640
So we're going to create one of those and learn how these work.

03:50.700 --> 03:56.550
So the first step is to create a new Cplusplus class for our async task.

03:56.550 --> 04:04.200
And I'm going to go into my ability system folder and we'll make a new folder for our async tasks.

04:04.200 --> 04:10.200
So I'm going to create a new Cplusplus class, choose all classes and add a blueprint.

04:10.230 --> 04:16.950
Async node will be based on Blueprint async action base.

04:16.950 --> 04:23.340
So we're going to choose Blueprint async action base and within the Ability system folder, I'm going

04:23.340 --> 04:32.250
to make another folder called Async Tasks, and this async task is going to be designed for listening

04:32.250 --> 04:34.980
or waiting for a cooldown.

04:34.980 --> 04:39.810
And we want to be informed when a cooldown begins and ends.

04:39.840 --> 04:48.270
In other words, a cooldown change so we can call this wait cooldown change, or we can call it listen

04:48.270 --> 04:49.770
for cooldown change.

04:50.100 --> 04:51.930
Either of those will work.

04:51.960 --> 04:58.140
I'm going to call it wait cooldown, change and create the class and I'll go ahead and close the editor

04:58.140 --> 05:00.360
down and save all.

05:00.480 --> 05:04.530
And let's open our new Cplusplus class.

05:04.530 --> 05:06.240
I'm going to close all tabs.

05:06.540 --> 05:14.600
Go into the ability system folder and here's my async tasks folder and here's wait cooldown change,

05:14.610 --> 05:18.120
here's the header file and we have our CPP file.

05:18.150 --> 05:26.010
Now, these are a lot like ability tasks in that we need delegates to broadcast and those delegates

05:26.010 --> 05:28.170
will become execution pins.

05:28.170 --> 05:34.560
And we also need a static function that can construct an instance of this class.

05:34.560 --> 05:37.020
So we have a couple things to create.

05:37.110 --> 05:40.650
Now, the first thing I'm going to create is a delegate.

05:40.650 --> 05:46.920
I'm going to declare a dynamic multicast delegate, and this delegate is going to broadcast a value.

05:46.950 --> 05:51.540
The value is going to be that cooldown duration, right?

05:51.540 --> 05:53.370
How long that cooldown should be.

05:53.370 --> 05:58.590
So our spell glow widget blueprint knows what value to show.

05:58.770 --> 06:05.010
So I'm going to use declare dynamic multicast.

06:06.640 --> 06:08.710
Delegate one param.

06:08.980 --> 06:19.600
I'm going to call this f cooldown change signature and it's going to broadcast a float and we'll call

06:19.600 --> 06:26.170
this time remaining as this will be how much time is left in the cooldown.

06:26.530 --> 06:35.520
Now I'm going to make a public section and we'll have two of these cooldown change signatures.

06:35.530 --> 06:42.400
In other words, I want to execution pins in my async node one for when our cooldown starts and one

06:42.400 --> 06:44.150
for when our cooldown ends.

06:44.170 --> 06:52.660
So I'm going to say f cooldown, change signature cooldown start and I'll have an F cooldown, change

06:52.660 --> 06:56.560
signature cooldown end.

06:57.970 --> 07:05.320
And these can be blueprint assignable so they get a new property with blueprint assignable both of them.

07:06.190 --> 07:14.680
So now we'll have an async task that will have two execution pins in addition to its regular output

07:14.680 --> 07:15.670
execution pin.

07:15.670 --> 07:20.650
And these will be executed once we broadcast those delegates.

07:20.680 --> 07:25.510
So now we need that static function, that factory that will construct one of these.

07:25.510 --> 07:27.150
So we're going to say static.

07:27.160 --> 07:33.730
It's going to return a wait cooldown, change object, and that's going to be returned by Pointer.

07:33.730 --> 07:38.920
And we're going to call this wait for cooldown change.

07:39.250 --> 07:41.140
Now wait for cooldown.

07:41.140 --> 07:43.800
Change is going to need to know some information.

07:43.810 --> 07:50.680
It needs the ability system component so it can listen for when something changes on that ability system

07:50.680 --> 07:55.600
component, specifically when a cooldown event has been applied.

07:55.630 --> 07:59.710
So the first input will be the ability system component.

07:59.710 --> 08:03.460
So it's going to be you ability system component.

08:03.910 --> 08:08.060
Now I'd like to forward declare that so we don't have to include it here.

08:08.270 --> 08:12.380
It's a pointer and we're going to call that ability system component.

08:12.710 --> 08:21.170
Now it also needs to know the cooldown gameplay tag as this specific node is going to be used in the

08:21.170 --> 08:21.980
spell globe.

08:21.980 --> 08:28.070
The spell globe needs to know what type of cooldown it's listening for, and these will be unique to

08:28.070 --> 08:30.350
the ability, the spell being cast.

08:30.350 --> 08:40.160
So it's going to take a const F gameplay tag reference called cooldown tag and it will only listen for

08:40.160 --> 08:42.400
that cooldown tag specifically.

08:42.410 --> 08:46.280
So we have a couple of inputs now it gets a new function.

08:46.280 --> 08:54.830
Obviously it has to be blueprint callable and because this is a task, this particular function is an

08:54.830 --> 08:56.000
internal function.

08:56.030 --> 09:02.810
These tasks, much like the ability task, result in creating an async task node.

09:02.840 --> 09:10.520
This function will be used to create an instance and that node, when executed, will result from that.

09:10.520 --> 09:18.260
So to specify that this is an internal use function, we use meta equals blueprint internal use only

09:18.260 --> 09:20.120
and set that equal to true.

09:20.210 --> 09:22.870
So there's our static factory function.

09:22.880 --> 09:30.260
Now if we're going to be listening for delegate broadcasts when this particular task ends.

09:30.290 --> 09:39.020
In other words, when that widget is destroyed, then we can clean up any resources in void end task.

09:40.180 --> 09:47.950
So we'll create this particular function and make it you function blueprint callable so that we can

09:47.950 --> 09:50.320
clean up anything when the task is done.

09:50.500 --> 09:56.650
Now, as soon as wait for cooldown, change is called and it receives an ability system component and

09:56.650 --> 09:57.720
a gameplay tag.

09:57.730 --> 10:02.650
We would like to store that in a member variable, so I'm going to make a protected section here.

10:04.590 --> 10:10.710
And in the protected section we're going to have a T object pointer to store the new ability system

10:10.710 --> 10:11.580
component.

10:11.580 --> 10:17.090
So I'm going to call this don't need that asterisk, I'm going to call it simply ASC.

10:17.280 --> 10:20.160
And this gets a new property.

10:22.220 --> 10:28.040
So it will be garbage collected or not, depending on the lifetime of the owner.

10:28.070 --> 10:30.620
We'll also have an gameplay tag.

10:32.740 --> 10:35.110
And this will be our cooldown tag.

10:35.850 --> 10:41.070
And I'll call the input parameter for weight cooldown change in cooldown tag.

10:41.070 --> 10:44.190
So there's no discrepancy between the names.

10:44.340 --> 10:49.440
So back in the CPP file, it looks like we haven't generated the definition yet, but we'll call it

10:49.470 --> 10:50.790
in cooldown tag.

10:50.790 --> 10:57.420
So the gameplay tag is not using the same name as that input parameter and we can go ahead and include

10:57.420 --> 11:00.780
the header for gameplay tag container.

11:00.810 --> 11:01.140
Okay.

11:01.140 --> 11:02.880
So we have our cooldown tag.

11:03.120 --> 11:08.160
So let's start thinking about how we want to implement, wait for cooldown, change.

11:08.160 --> 11:13.560
Let's go ahead and generate the definition and this is going to be similar to how our ability tasks

11:13.560 --> 11:14.100
work.

11:15.090 --> 11:20.250
Essentially we create a new object based on weight cooldown change.

11:20.340 --> 11:22.320
Now in ability tasks.

11:22.320 --> 11:26.320
We used a function to construct our new task.

11:26.340 --> 11:33.390
If we look at target data under mouse and create target data under mouse, we used new ability task,

11:33.390 --> 11:37.650
but we're not making an ability task in weight cooldown change.

11:37.650 --> 11:39.030
I'm going to close that tab.

11:39.030 --> 11:43.500
We're making a new object, so we use new object for this.

11:43.530 --> 11:50.880
We're going to say you weight cooldown, change, We're going to call this weight cooldown change.

11:53.320 --> 12:00.100
And we use new object, which is a template requiring the type it's going to be you wait, cooldown,

12:00.100 --> 12:01.030
change.

12:01.720 --> 12:03.400
So we call it like that.

12:03.760 --> 12:06.640
And once we've created one, we can set its member variables.

12:06.640 --> 12:09.210
We need to set that as key.

12:09.220 --> 12:14.320
So we're going to say, Wait, cooldown change ASC equals we're going to set it to the ability system

12:14.320 --> 12:18.430
component and wait cooldown, change cooldown, tag.

12:19.030 --> 12:20.350
We're going to take that tag.

12:20.350 --> 12:24.880
We're going to set it to the gameplay tag passed in in cooldown tag.

12:24.970 --> 12:27.430
So we'll go ahead and set that as well.

12:27.760 --> 12:34.810
Now, right away, if the ability system component isn't valid and for that matter, if the cooldown

12:34.810 --> 12:40.450
tag passed in is not valid, well then we can just call end task and just end the whole thing.

12:40.450 --> 12:45.940
So end task is something I'd like to generate a definition for and I'll just call that.

12:45.940 --> 12:53.690
So we're going to say if is valid, we'll check that ability system component and we'll end the task

12:53.690 --> 12:56.300
and return if that's not valid.

12:56.300 --> 13:02.210
So we'll put a not there, but we're going to have an Or as well and we'll have our in cooldown tag

13:02.210 --> 13:03.830
and it's a gameplay tag.

13:03.830 --> 13:05.720
So those have their own is valid.

13:05.720 --> 13:09.440
We're going to go ahead and put not is valid there.

13:09.530 --> 13:11.570
If any of those happen, we'll return.

13:11.570 --> 13:18.890
But I'm going to have to include the ability system component H header file.

13:18.890 --> 13:27.410
So if this condition happens then I'm going to take my newly created wait cooldown change and just call

13:27.410 --> 13:31.100
it end task and then we'll return null pointer.

13:32.210 --> 13:34.310
So that takes care of an early out.

13:34.340 --> 13:40.880
Now if we get past this point we have a valid ability system component and cooldown tag that was passed

13:40.880 --> 13:41.630
in here.

13:41.810 --> 13:43.520
So what do we want to do?

13:43.520 --> 13:51.410
We want to do something when this ability system component has a cooldown event added to it.

13:51.410 --> 13:57.200
And we also want to do something when that ability system component has a cooldown removed.

13:57.200 --> 13:59.570
So how can we do that?

13:59.570 --> 14:07.580
Well, we can know if a cooldown tag has been added by subscribing to a specific delegate on the ability

14:07.580 --> 14:14.870
system component so we can take the ability system component that's been passed in and we can use register

14:14.900 --> 14:16.430
gameplay tag event.

14:16.460 --> 14:20.000
Now we've used this before to listen for tag events, haven't we?

14:20.030 --> 14:23.840
See if you can rack your brain and remember where we've used this before.

14:24.020 --> 14:25.990
If you go to Aura enemy.

14:26.550 --> 14:29.370
Remember, we have this hit react tag changed.

14:30.280 --> 14:36.190
And if we search for that, we bound to this and begin play for the enemy.

14:36.220 --> 14:43.750
We use register gameplay tag event binding the enemies hit react tag change so that way it knows when

14:43.750 --> 14:50.350
it's hit reacting or not based on when the tag count changes, when it's greater than zero or when it

14:50.350 --> 14:51.340
becomes zero.

14:51.370 --> 14:53.540
We'll use that same concept here.

14:53.560 --> 15:01.600
So I'd like a function to call as a callback when the cooldown tag has changed on our ability system

15:01.600 --> 15:02.540
component.

15:02.560 --> 15:07.210
So this is going to be similar to that function.

15:07.210 --> 15:08.830
Hit react tag changed.

15:09.940 --> 15:17.500
And remember, hit react tag changed took a gameplay tag and a int 32 called a new count.

15:17.620 --> 15:24.280
Now this delegate that our callback will bind to needs this to be a const gameplay tag without being

15:24.280 --> 15:25.810
a const reference strangely.

15:25.810 --> 15:26.290
Right.

15:26.290 --> 15:29.180
So that's the kind of signature we have to have.

15:29.200 --> 15:32.390
We're going to make our function with that signature.

15:32.390 --> 15:39.950
So we're going to put this down here in the protected section and we'll call this function void cooldown

15:39.950 --> 15:41.690
tag changed.

15:41.780 --> 15:43.970
Just like hit react tag changed.

15:43.970 --> 15:46.550
So it'll take a const gameplay tag.

15:48.520 --> 15:58.450
Called in cooldown tag just like for weight cooldown change and also it'll take in an int 32 called

15:58.450 --> 15:59.320
new count.

16:00.510 --> 16:07.770
So we can go ahead and generate the definition and we can register gameplay tag event.

16:07.800 --> 16:11.670
Now register gameplay tag event takes a gameplay tag.

16:11.670 --> 16:14.450
So we're going to be listening for a specific tag.

16:14.460 --> 16:16.560
We know the tag we want to listen for.

16:16.560 --> 16:18.030
It's our cooldown tag.

16:18.030 --> 16:19.230
We're going to use that.

16:19.230 --> 16:22.230
So in cooldown tag will be the first input.

16:22.260 --> 16:26.310
Now we also have a gameplay tag event type, right?

16:26.340 --> 16:34.650
So if we type a gameplay tag event type double colon, we can see that we can use any count change or

16:34.680 --> 16:35.760
new or removed.

16:35.760 --> 16:37.620
I'd like to use new or removed.

16:37.620 --> 16:44.490
So this function call will return that delegate and at the very end here we can use Dot and we can add

16:44.490 --> 16:48.840
you object and bind to the cooldown tag changed.

16:48.870 --> 16:54.900
Now we're within a static function, so we can't just use this as this doesn't really mean anything

16:54.900 --> 16:56.040
in a static function.

16:56.040 --> 17:00.670
This returns a pointer to the current object we're in, but static functions mean well.

17:00.670 --> 17:04.300
We can call this even when one of these doesn't even exist.

17:04.330 --> 17:09.220
What we have to do is use the user object that we just created.

17:09.250 --> 17:11.680
That's our wait cooldown change object.

17:11.680 --> 17:16.450
So we're going to pass in wait cooldown, change instead of this.

17:16.660 --> 17:20.170
And the function that we created are callback.

17:20.200 --> 17:23.620
You wait cooldown, change, cooldown tag changed.

17:24.470 --> 17:27.230
And that's passed in by address.

17:27.230 --> 17:30.640
So the address of operator followed by that function.

17:30.650 --> 17:34.250
So that's what we're going to bind there.

17:34.310 --> 17:37.520
So I'll go ahead and just show the whole line here.

17:37.700 --> 17:43.640
And if we like, we can put these on their own lines so we can really see what's going on.

17:45.930 --> 17:46.740
There we go.

17:46.740 --> 17:49.920
So we're calling registered gameplay tag event passing.

17:49.920 --> 17:57.090
In the cooldown tag we'd like to listen for we're going to use new or removed and we're adding the function

17:57.090 --> 18:02.790
on our newly created wait cooldown change object called cooldown Tag changed.

18:02.910 --> 18:09.750
Now what we do after creating one of these for our async task is we return that object.

18:09.750 --> 18:17.280
So we're going to say return, wait, cooldown, change, and now we'll have a blueprint async task

18:17.280 --> 18:21.390
node that will result in calling this function and it'll set it all up.

18:21.390 --> 18:28.440
That task created will have its ability system component and cooldown tag set and it'll also have its

18:28.440 --> 18:35.850
cooldown tag changed callback bound to that ability system component when it broadcasts that delegate

18:35.910 --> 18:38.370
now an end task, we can clean this up.

18:38.370 --> 18:46.020
We can actually remove that specific delegate from the ability system component.

18:46.020 --> 18:54.000
Well we can do is we can take our AC variable as we're storing that ability system component after all,

18:54.000 --> 19:03.990
and we can take that register gameplay tag event passing in the cooldown tag and the gameplay tag event

19:04.020 --> 19:07.110
type that we used which was new or removed.

19:07.110 --> 19:10.890
This will get that delegate and we can call remove.

19:10.890 --> 19:19.620
We can actually call remove all on it using this because it requires the user object.

19:19.650 --> 19:26.610
So what it'll do is it'll get that ability system component and remove that callback from its delegate

19:26.610 --> 19:27.000
list.

19:27.000 --> 19:31.680
So delegates keep a list of all functions they should call when they're broadcast.

19:31.710 --> 19:32.910
This will remove it.

19:32.910 --> 19:34.680
So this kind of cleans that up.

19:34.680 --> 19:42.930
Now, before we go and use the AC, we could be safe about it and say if is valid AC, we can even just

19:42.930 --> 19:44.010
return.

19:44.010 --> 19:51.060
So if not is valid, simply return and then we can remove that delegate.

19:51.090 --> 19:58.770
Now these async tasks have a function called set ready to destroy, and if we hover over it, we can

19:58.770 --> 20:02.370
see it says call when the action is completely done.

20:02.400 --> 20:06.990
This makes the action free to delete and will unregister it with the game instance.

20:06.990 --> 20:14.820
So we can do that in our end task and we can also call Mark as garbage and that does what it sounds

20:14.820 --> 20:15.120
like.

20:15.120 --> 20:16.980
It marks it for deletion.

20:17.720 --> 20:19.490
Okay, so what do we do in cooldown?

20:19.490 --> 20:20.600
Tag changed?

20:20.630 --> 20:24.290
Well, we know we have delegates that we're going to want to broadcast, right?

20:24.410 --> 20:31.460
And when the cooldown tag has changed, this is the situation where we can know if the cooldown has

20:31.460 --> 20:34.640
been removed, how well we can check that new count.

20:34.670 --> 20:40.910
If new count is equal to zero, then we know that the tag has been removed so we can use one of our

20:40.910 --> 20:41.420
delegates.

20:41.420 --> 20:44.360
The cooldown end delegate.

20:44.930 --> 20:46.220
Cooldown, end.

20:46.310 --> 20:48.140
And we can broadcast.

20:48.230 --> 20:51.620
And this requires that time remaining.

20:51.620 --> 20:52.210
Right.

20:52.220 --> 20:54.950
So what do we broadcast for that time remaining?

20:54.950 --> 20:57.410
Well, we're out of time, right?

20:57.440 --> 21:02.660
The cooldown has zero seconds left on it, which means the cooldown is over.

21:02.960 --> 21:09.320
So that's how we can broadcast that delegate and execute that cooldown end execution pin.

21:09.320 --> 21:11.630
But what about cooldown begin?

21:11.660 --> 21:14.930
How can we know when that has happened?

21:15.260 --> 21:21.510
Well, for that we can use a different delegate that gets broadcast on the ability system component.

21:21.510 --> 21:31.500
That delegate is broadcast whenever a gameplay effect is added so that delegate we can bind it here

21:31.500 --> 21:38.790
is we're going to take our ability system component and get on active gameplay effect added delegate

21:38.790 --> 21:39.360
to self.

21:39.360 --> 21:44.400
We've used this one as well and we can right click and we can just check it out.

21:44.430 --> 21:49.950
It says called on both client and server whenever a duration based GE is added.

21:49.950 --> 21:53.250
So we actually haven't used this one specifically.

21:53.280 --> 22:00.030
We've used the one that's called whenever a gameplay effect is applied to self on gameplay effect,

22:00.030 --> 22:01.320
apply delegate to self.

22:01.320 --> 22:07.410
We've used this one which is only called on the server and we use that because this is for all effects,

22:07.410 --> 22:10.380
instant duration, infinite whatever.

22:10.380 --> 22:15.900
But this one is for only duration based gameplay effects and it's called on both client and server.

22:15.900 --> 22:16.650
So that's great.

22:16.650 --> 22:20.100
We don't have to worry about any rpcs or anything like that.

22:20.100 --> 22:23.370
So this is going to be a really nice delegate to use.

22:23.400 --> 22:29.580
Now we're going to use that and we're going to bind a callback to it, but that means we need a callback,

22:29.580 --> 22:30.030
right?

22:30.030 --> 22:38.190
So if we go to the declaration or usages, we right click on the type of this delegate and go to declaration

22:38.190 --> 22:38.790
or usages.

22:38.790 --> 22:45.030
We can see that a callback for this delegate needs to take in an ability system component, a gameplay

22:45.030 --> 22:47.880
effect spec and an active gameplay effect handle.

22:47.880 --> 22:49.920
So it's going to need three inputs.

22:49.920 --> 22:53.240
So that's what our callback is going to need to have.

22:53.250 --> 22:56.160
So let's go ahead and create that callback function.

22:56.160 --> 23:01.650
We'll have it right here, we'll call it void on active effect added.

23:02.510 --> 23:07.030
Now this is going to take a new ability system component pointer.

23:07.040 --> 23:09.380
We can call it Target ASC.

23:09.770 --> 23:16.430
And if we go back and look, we see that next it needs a const gameplay effect spec reference.

23:16.430 --> 23:18.290
So that will be the next input.

23:18.320 --> 23:20.630
We're going to call this spec applied.

23:21.290 --> 23:26.180
And finally we need the active gameplay effect handle.

23:26.820 --> 23:28.200
So we'll put that there.

23:28.200 --> 23:31.350
And this will be called active effect handle.

23:33.230 --> 23:35.720
So let's generate the definition for this.

23:36.750 --> 23:37.860
There we go.

23:37.890 --> 23:41.100
We see that this is an incomplete type, of course.

23:41.130 --> 23:42.690
It's an active gameplay effect.

23:42.720 --> 23:43.260
Handle that.

23:44.070 --> 23:45.330
We can include that.

23:45.330 --> 23:47.490
And this can't be a reference.

23:47.490 --> 23:50.610
So we can just include it in the h file here.

23:50.660 --> 23:53.400
Looks like it created our function definition inline.

23:53.400 --> 23:54.540
That's not what I wanted.

23:54.540 --> 24:00.150
So first I'm going to cut it and paste it into the CPP file right there.

24:00.150 --> 24:08.220
And then in the header file I'm going to include the header for that type and it's active gameplay effect

24:08.250 --> 24:09.660
handle dot h.

24:10.410 --> 24:12.900
So that'll take care of that red squiggle.

24:13.110 --> 24:16.440
And now we have on active effect added.

24:16.650 --> 24:17.910
We're going to bind this.

24:17.910 --> 24:21.300
So back up to wait for cooldown change.

24:21.300 --> 24:22.860
We have this delegate.

24:22.890 --> 24:30.120
We're going to call, add, object and pass in the instance that we just created of this class that's

24:30.150 --> 24:31.890
wait, cooldown, change.

24:31.890 --> 24:34.140
So that's the user object.

24:34.140 --> 24:42.030
And then the address of the function callback we created on active effect added That's a fully qualified

24:42.030 --> 24:44.700
function with the class name and everything.

24:44.700 --> 24:50.700
So this is what we'll use to know when an effect has been added.

24:50.700 --> 25:02.550
So we'll say to know when a cooldown effect has been applied and this will be to know when a cooldown

25:02.550 --> 25:05.670
has ended In parentheses.

25:05.700 --> 25:09.330
Cooldown tag has been removed.

25:09.600 --> 25:13.280
So now what do we do then in this callback?

25:13.300 --> 25:15.550
Well, we're going to broadcast, right?

25:15.550 --> 25:22.480
We need to broadcast our delegate cooldown start, but this also requires the time remaining.

25:22.480 --> 25:24.730
So how do we get that time remaining?

25:24.820 --> 25:30.790
Well, we can check the gameplay effect using our spec applied and we're going to check the gameplay

25:30.790 --> 25:37.420
effect for its granted tags and we're also going to check its asset tags and see if any of those contain

25:37.420 --> 25:41.110
a cooldown tag and specifically our cooldown tag.

25:41.110 --> 25:42.610
That's our member variable.

25:42.940 --> 25:45.130
So we can take the spec applied.

25:45.790 --> 25:51.520
So we say spec applied dot and we can get all asset tags.

25:51.550 --> 25:59.950
Now this requires a gameplay tag container so we can make one for gameplay tag container and we'll call

25:59.950 --> 26:03.220
this asset tags and we can pass that in.

26:05.160 --> 26:09.210
Now we can also get all granted tags and we'll do the same thing.

26:09.210 --> 26:16.080
We're going to first create an gameplay tag container and we'll call this granted tags and we'll take

26:16.080 --> 26:23.970
our spec applied and we'll call get all granted tags passing in, granted tags.

26:25.350 --> 26:30.720
So now we have to gameplay tag containers and we can check to see if any of them contain our cooldown

26:30.720 --> 26:40.110
tag so we can say if asset tags dot has tag and we'll use has tag exact checking for the cooldown tag,

26:40.230 --> 26:43.170
but it might be in the granted tag.

26:43.170 --> 26:50.880
So we're going to say or granted tags dot has tag exact and this will also check for cooldown tags.

26:50.880 --> 26:57.990
So if either of them have that tag, then we know that the effect that has just been applied is a cooldown

26:57.990 --> 26:58.800
effect.

26:58.800 --> 27:07.480
So in that case we want to broadcast our cooldown, start delegate, but how do we get that time remaining?

27:07.510 --> 27:09.150
That's the question.

27:09.160 --> 27:12.790
Well, the ability system component has functions for doing that.

27:12.790 --> 27:20.050
We can take the ask and there's a function called get active effects duration.

27:20.080 --> 27:26.440
There's get active effect, event set, end time, time remaining and so on.

27:26.440 --> 27:31.360
So if we call get active effects time remaining, well, what does it take?

27:31.390 --> 27:33.520
It takes a gameplay effect query.

27:33.550 --> 27:40.870
So I'm going to comment this out as we create that query and it's an gameplay effect query, we can

27:40.870 --> 27:42.910
call it gameplay effect query.

27:43.090 --> 27:45.730
And what do we set this equal to?

27:45.760 --> 27:50.140
Well, there's a static function to make a gameplay effect query.

27:50.170 --> 27:57.910
It exists on gameplay effect query and it's called make query and there's a bunch of different kinds

27:57.910 --> 27:59.050
of make query.

27:59.050 --> 28:07.510
In fact, we can make a query to specify match all effect tags match all owning tags match any effect

28:07.540 --> 28:08.260
tags.

28:08.290 --> 28:11.110
What we want is match any owning tags.

28:11.200 --> 28:18.790
So if we get active effects time remaining, our query will say get any effects that match this specific

28:18.790 --> 28:19.780
cooldown tag.

28:19.780 --> 28:26.650
And if we set this up right, all of our gameplay abilities that have cooldowns should each use a unique

28:26.650 --> 28:29.230
cooldown tag and that way this will work.

28:29.230 --> 28:34.570
Now we're going to check to see if we match the specific cooldown tag.

28:34.570 --> 28:37.570
But notice that this requires a gameplay tag container.

28:37.570 --> 28:41.140
So we need a container that contains just our cooldown tag.

28:41.290 --> 28:46.930
Well, there's a handy function to create a gameplay tag container that just contains one tag.

28:46.930 --> 28:53.020
We can do so by taking the cooldown tag and we can call get single tag container.

28:53.260 --> 28:59.170
So this function returns a container with the single tag, the one that we called this from.

28:59.350 --> 29:00.400
So there we have it.

29:00.400 --> 29:07.180
We have a query and our ability system component has the function we can call get active effects time

29:07.180 --> 29:07.630
remaining.

29:07.630 --> 29:15.280
We can pass in that gameplay effect query and this function will return some information for us.

29:15.280 --> 29:16.420
What does it return?

29:16.420 --> 29:19.270
Well, it returns a t array of floats.

29:19.540 --> 29:20.800
Interesting, right?

29:20.800 --> 29:23.470
It returns a t array of floats.

29:23.500 --> 29:24.970
Now, why is it a t array?

29:24.970 --> 29:26.290
Why isn't it just a float?

29:26.290 --> 29:32.410
Well, that's because this function will return the time remaining for all gameplay effects that have

29:32.410 --> 29:34.330
any of the owning tags.

29:34.330 --> 29:39.340
So we could have multiple active gameplay effects going on at the same time.

29:39.340 --> 29:45.580
And if any of them have any owning tags that match this container, then they'll be in this array.

29:45.580 --> 29:52.270
So we need to create a t array of floats, but we know it's only going to have one.

29:52.270 --> 29:54.430
So t array of floats.

29:54.430 --> 30:00.250
We're going to call this times remaining, but we know it's only going to have one.

30:00.250 --> 30:10.800
So what we'll say is if time's remaining dot num is greater than zero, really it should be one.

30:10.830 --> 30:18.870
Then we know the first element of it is our cooldown time remaining so we can say float time remaining

30:19.170 --> 30:23.730
equals times remaining of zero.

30:23.880 --> 30:27.630
So as you can see, this is actually quite a bit more versatile.

30:27.630 --> 30:34.260
We could get all the active effects that have a specific cooldown tag and we'll have all of their times

30:34.260 --> 30:40.950
remaining, but we're only going to have one cooldown tag per type at a time, right?

30:40.950 --> 30:45.030
We'll only have one fireball cooldown tag applied at a time.

30:45.030 --> 30:46.050
At least we should.

30:46.260 --> 30:51.570
Now, there's a way to just in case we have multiples, right?

30:51.570 --> 30:58.650
For some reason we have multiple tags at the same time that maybe haven't been cleared out in time.

30:58.650 --> 30:59.100
Right.

30:59.130 --> 31:03.780
We could get the time remaining from this array with the greatest.

31:03.880 --> 31:07.180
Time just to make sure that we're getting the right one.

31:07.180 --> 31:12.820
So as a safeguard, we're going to write a little algorithm that will get out of this array.

31:12.820 --> 31:17.280
As long as it's greater than zero, we'll get the greatest time remaining.

31:17.290 --> 31:21.370
So to do that, we make a little bit of a maximum algorithm.

31:21.370 --> 31:25.600
We make an INT 32 called index, and we started off at zero.

31:25.600 --> 31:34.780
So we start at the first index of our array and then we have a float called Longest or Highest.

31:36.950 --> 31:41.630
Time and we start that off at the time at the first index.

31:41.630 --> 31:44.420
So we say time's remaining.

31:45.350 --> 31:46.310
Of zero.

31:46.880 --> 31:48.710
And then we do a for loop.

31:48.740 --> 31:55.880
So we're going to say for n 32 I equals zero, we're going to loop through.

31:55.880 --> 32:00.980
I is less than times remaining dot num.

32:02.030 --> 32:04.190
We'll do I plus plus.

32:04.730 --> 32:16.460
So regular old for loop and we see if time's remaining at I is greater than highest time.

32:17.240 --> 32:20.720
So if it is this will be a if.

32:23.060 --> 32:26.030
Then we know that that is higher than highest time.

32:26.030 --> 32:31.700
So we say highest time equals times remaining of I.

32:32.420 --> 32:36.200
And then we know that the index is I.

32:37.030 --> 32:41.860
So your standard find the maximum value in an array algorithm.

32:41.860 --> 32:44.170
This is something that is pretty common.

32:44.260 --> 32:49.060
So by the end of this for loop, we're going to know that the correct time.

32:49.090 --> 32:52.990
Time remaining is going to be the highest time.

32:53.110 --> 33:00.180
So just in case it's not the first element, element zero, it's going to be highest time.

33:00.190 --> 33:03.910
And we, of course, don't really need index, do we?

33:03.940 --> 33:08.770
We know the index of the highest time, but we don't really actually use it.

33:08.770 --> 33:16.180
So all we do is step through the array and if any element is higher than highest time, it replaces

33:16.180 --> 33:20.290
highest time and by the end time remaining as high as time.

33:20.440 --> 33:22.660
So we really don't even need time remaining.

33:22.660 --> 33:27.070
We just have highest time and we can just call that time remaining.

33:27.070 --> 33:27.400
Right?

33:27.400 --> 33:30.370
So time remaining.

33:33.180 --> 33:34.110
Like so.

33:34.110 --> 33:40.680
And once we know time remaining, well, we can finally broadcast our delegate, which is called cooldown

33:40.680 --> 33:41.130
start.

33:41.130 --> 33:47.370
So we can say cooldown, start dot broadcast and we can broadcast time remaining.

33:49.590 --> 33:50.070
Okay.

33:50.070 --> 33:52.800
So that was kind of a lot to set up.

33:52.800 --> 33:58.620
But now that we have this class, let's go ahead and run and see what happens when we try to create

33:58.650 --> 34:00.910
one of these tasks in Blueprint.

34:00.930 --> 34:04.250
So we have a couple of errors here.

34:04.260 --> 34:09.150
Missing type specifier int assumed that's wait cool down change dot H.

34:09.180 --> 34:12.390
This means we have an issue with the types that we're using.

34:12.390 --> 34:17.700
We have gameplay effects spec we haven't included that we're going to go ahead and forward declare.

34:17.700 --> 34:21.960
That's a struct f gameplay effect spec.

34:27.460 --> 34:28.510
So that fixed it.

34:28.540 --> 34:30.730
Let's go ahead and run and launch the editor.

34:31.150 --> 34:31.630
Okay.

34:31.630 --> 34:36.520
So I'm going to get my spell globe back open and go to the event graph here.

34:36.520 --> 34:43.240
And what I'd like to do is as soon as our widget controller is set, I'd like to create one of these

34:43.240 --> 34:49.780
tasks we just created and the function that we expose to blueprint is called Wait for Cooldown Change.

34:49.780 --> 34:53.050
We're going to call, wait for cooldown, change.

34:53.050 --> 34:54.100
And here it is.

34:54.130 --> 34:55.570
It's under the category.

34:55.570 --> 34:57.040
Wait, cooldown, change.

34:57.040 --> 34:58.570
And here's our async task.

34:58.570 --> 35:03.070
And look, here's cooldown start, here's cooldown end and here's time remaining.

35:03.100 --> 35:07.420
Now the thing is we need to know what cooldown tag to listen for.

35:07.420 --> 35:12.700
So really each spell globe needs to have its own cooldown tag assigned to it.

35:12.730 --> 35:19.800
Now the problem with that is we don't know what the cooldown tag is until our spell globe has received

35:19.810 --> 35:21.040
ability info.

35:21.070 --> 35:24.250
That's when it knows what ability is assigned to it.

35:24.310 --> 35:29.810
So it's going to need to receive that ability info perhaps in our info struct.

35:29.810 --> 35:30.830
If I break it.

35:30.860 --> 35:33.260
We have an ability tag, an input tag.

35:33.260 --> 35:38.270
We now kind of need a cooldown tag as well, so we'll do that in the next video.

35:38.270 --> 35:41.690
For now though, let's just hard code a cooldown tag.

35:41.690 --> 35:48.680
I'm going to make a little gameplay tag and set this to the only cooldown that will be receiving that's

35:48.680 --> 35:51.530
going to be cooldown Fire Firebolt.

35:51.650 --> 35:58.430
And we'll add this to our sequence here and we're going to need the ability system component.

35:58.430 --> 36:00.170
So how are we going to get that?

36:00.200 --> 36:03.290
Well, remember, we have a widget controller, right?

36:03.290 --> 36:06.500
And it's cast to BP overlay widget controller.

36:06.500 --> 36:14.150
So if we get our BP overlay widget controller, remember that it has an ability system component, we

36:14.150 --> 36:15.350
can pass that in.

36:15.560 --> 36:20.000
Isn't it convenient to have a widget controller that has the ability system component?

36:20.000 --> 36:21.320
It's super convenient.

36:21.320 --> 36:25.280
So what we can do now is just test this out.

36:25.310 --> 36:30.110
Remember, we have lots of spell globes and none of them are checking against the cooldown tag.

36:30.110 --> 36:33.320
So we'll see lots of print strings if we use a print string here.

36:33.320 --> 36:34.880
But I still want to do it.

36:34.880 --> 36:41.030
I want to print cooldown, start when the cooldown starts and I want to print.

36:41.670 --> 36:47.750
Cooldown and when it ends, so cooldown end here.

36:47.760 --> 36:55.590
Let's just see what happens when we save all press play and what happens when we start a cooldown.

36:55.590 --> 36:57.090
So we have to cast a spell.

36:58.220 --> 36:59.270
So there we have it.

36:59.270 --> 37:07.340
And it was kind of quick, but if we go into the folder that has our ability and specifically our cooldown,

37:07.340 --> 37:08.780
we can change the cooldown.

37:08.780 --> 37:10.010
Let's make it longer.

37:10.130 --> 37:12.080
0.5 seconds is hard to test.

37:12.080 --> 37:14.030
I'm going to make it three seconds.

37:14.030 --> 37:20.240
So that way we should see if we launch a firebolt we see start three seconds later, we should see end

37:20.240 --> 37:22.070
and there's end.

37:22.070 --> 37:30.290
So all of our spell globes now know when a cooldown has started and ended specifically for the firebolt.

37:30.290 --> 37:35.930
And if this were some other gameplay tag, let's pass in damage instead.

37:35.930 --> 37:41.810
Well then it's not going to do anything right because there are no effects that have that damage tag

37:41.810 --> 37:45.110
where specifically listening for the cooldown tag.

37:46.280 --> 37:52.670
Now, this is great, but each of our spell globes needs to know what cooldown tag it should listen

37:52.670 --> 37:53.240
for.

37:53.240 --> 37:59.000
So it's going to need its own cooldown tag variable and it's going to need to receive that when it receives

37:59.000 --> 38:00.110
ability info.

38:00.110 --> 38:04.340
So that's what we'll do in the next video because this video has already gotten quite long.

38:04.340 --> 38:10.250
So in the next video we'll make each spell globe know which cooldown it's listening for and then we'll

38:10.250 --> 38:15.560
handle updating its cooldown timer and showing this visually in the HUD.

38:15.980 --> 38:18.740
Excellent job and I'll see you in the next video.
