WEBVTT

00:06.780 --> 00:07.980
Welcome back.

00:08.190 --> 00:14.580
Now, here in our Attributes set, we're checking to see if the changed attribute here in post gameplay

00:14.580 --> 00:19.440
effect execute is incoming XP our meta attribute.

00:19.470 --> 00:28.110
Now, if it is, we're adding to XP using the player interface that allows us to actually change our

00:28.110 --> 00:29.880
XP on the player state.

00:29.880 --> 00:35.970
But now that we're concerned with leveling up, we need to check to see if this is enough for a level

00:35.970 --> 00:36.480
up.

00:36.510 --> 00:39.780
If it is, we want to level up the character.

00:39.780 --> 00:43.440
Now what happens as a result of that depends.

00:43.440 --> 00:51.780
Yes, we're going to award certain values to various classes so that we can increase our level of progression,

00:51.780 --> 00:58.500
but also we're going to most likely have some visual effects and in particular some effects related

00:58.500 --> 01:00.270
to the player specifically.

01:00.390 --> 01:04.580
So I'd like to add another player interface function for leveling up.

01:04.590 --> 01:11.470
So I'm going to go into my public folder, into interaction and into player interface, and we're going

01:11.470 --> 01:15.400
to add another function to this, and this one will be for leveling up.

01:15.490 --> 01:20.650
Now it's going to be void and I'm just going to call it level up and we can implement this however we

01:20.650 --> 01:23.290
like in our Aura character.

01:23.290 --> 01:26.200
And I'm also going to make it a Blueprint Native event.

01:27.490 --> 01:32.830
So we can override this in aura character however we like.

01:32.860 --> 01:38.680
For now, I'm just going to override it and then we'll decide what happens as a result of calling this

01:38.680 --> 01:39.350
later.

01:39.370 --> 01:46.900
So we can say virtual void level up implementation override and we can generate an empty definition.

01:46.900 --> 01:48.640
We don't need to call super for this.

01:48.670 --> 01:51.760
We're going to do whatever we want here.

01:51.790 --> 01:54.340
Most likely going to be cosmetic.

01:54.490 --> 02:01.240
But now that we have this function, we can decide in our attributes set if we've just gained enough

02:01.240 --> 02:05.950
XP to level up and that's going to involve some kind of calculation.

02:05.980 --> 02:08.020
That's why we have our to do here.

02:08.020 --> 02:09.660
So we'll do that.

02:09.670 --> 02:12.520
I'd like to do that next in our next video.

02:12.520 --> 02:16.840
But before we wrap up this one, I'd like to do one more thing.

02:16.840 --> 02:24.490
I'd like to go into my interaction folder again into combat interface and notice that we have a lot

02:24.490 --> 02:30.710
of Blueprint Native events and we've seen that this is really nice because it allows us to avoid the

02:30.710 --> 02:34.130
cast to the interface and call a static function.

02:34.160 --> 02:41.000
The execute version related to this, but get player level is not one of these and we're using get player

02:41.000 --> 02:46.790
level all over the place and I really prefer that it be one of these blueprint native events so that

02:46.790 --> 02:51.110
we can execute it without a cast just like the others.

02:51.140 --> 02:57.950
That way in our aura attributes set where we're calling get player level, we can be consistent about

02:57.950 --> 03:00.470
it here and send Exp event.

03:00.470 --> 03:06.200
For example, we're casting to the combat interface just so we can call get player level.

03:06.260 --> 03:11.150
If get player level were a blueprint native event, we wouldn't have to do that.

03:11.150 --> 03:17.600
We could just check to see if the target character implements the interface and then call execute get

03:17.600 --> 03:18.380
character class.

03:18.380 --> 03:19.940
Now this isn't a problem.

03:19.940 --> 03:21.920
This is a totally legit way to do it.

03:21.920 --> 03:27.800
It's not bad practice or anything like that, but I would prefer if this were consistent here, if I

03:27.800 --> 03:30.410
got both of these in a consistent manner.

03:30.410 --> 03:32.960
That's one reason I'd like to change it.

03:32.990 --> 03:41.060
But another reason I'd like to change it is because complications can occur when we're relying on something

03:41.090 --> 03:42.680
to implement an interface.

03:42.680 --> 03:44.960
And I'm going to show you one of those.

03:45.050 --> 03:50.720
If we go into the private folder and ability system and open exec calc damage.

03:51.120 --> 03:58.200
Here in exact calc damage all the way up at the top where casting the source, Avatar and target Avatar

03:58.200 --> 04:00.960
both to these combat interfaces.

04:00.990 --> 04:06.420
Now, that's fine if they both implement those and if they didn't, well, we could perform a null check

04:06.420 --> 04:07.320
or something like that.

04:07.320 --> 04:10.350
But why are we using the combat interface?

04:10.350 --> 04:17.010
Well, if we search this file for interface, we can see that we're calling get player level here and

04:17.010 --> 04:22.650
we're evaluating curves from curve tables based on the player level that we get.

04:22.830 --> 04:23.880
Now, this is great.

04:23.880 --> 04:30.300
Again, if both the damage causer and the receiver implement this combat interface, this works just

04:30.300 --> 04:30.960
fine.

04:30.960 --> 04:34.830
But if let's say we're getting damage from an effect.

04:34.830 --> 04:35.900
Actor Right.

04:35.940 --> 04:43.080
A fire area, well, that fire area may or may not implement this interface, at which point we'd be

04:43.080 --> 04:45.450
trying to cast to the interface.

04:45.450 --> 04:51.670
The interface would be a null pointer in this case and we'd be trying to access a null pointer again.

04:51.670 --> 05:00.310
We could perform a null check, but it'd be nicer if we could just check to see if these actors implement

05:00.310 --> 05:06.640
the interface and then have some local level variable that we can set to zero if they don't implement

05:06.640 --> 05:07.510
the interface.

05:07.510 --> 05:11.260
And then we would just have zero in our calculations.

05:11.470 --> 05:18.070
So what I'd like to do is change the function in the combat interface, get player level so that it's

05:18.070 --> 05:19.510
a blueprint native event.

05:19.510 --> 05:25.600
And of course that's going to require us to go through the project everywhere it's used and change it.

05:25.630 --> 05:33.550
We're going to have to change what we're overriding in both a character and or an enemy to the implementation

05:33.550 --> 05:34.270
version.

05:34.270 --> 05:39.700
And then everywhere we're calling the function, we have to call the static execute version instead.

05:39.700 --> 05:45.730
So this will require a little bit of a project refactor, not a big one, but a little one.

05:45.730 --> 05:48.640
So I'd like to do that in this video.

05:48.640 --> 05:54.690
And this can happen in a project from time to time where you decide to change a design.

05:54.700 --> 06:00.970
So the first thing we need to do is we're going to remove virtual from the function and we're going

06:00.970 --> 06:04.150
to add a new function with Blueprint Native event.

06:04.180 --> 06:05.680
That's the first thing.

06:06.010 --> 06:10.120
Now as soon as we've done that, we're going to create lots of errors.

06:10.120 --> 06:14.640
If we go ahead and compile right now, we'll get compiler errors.

06:14.650 --> 06:21.490
So the first errors we get are in or character and or enemy like I just mentioned, get player level.

06:21.490 --> 06:24.250
This is no longer something we override.

06:24.250 --> 06:28.720
Instead we override the underscore implementation version of this.

06:28.720 --> 06:36.430
So we have to do this both in the H file and we have to go into the CPP file and change it here as well.

06:36.430 --> 06:43.210
So I just did for or a character we can do so for Ora Enemy as well, changing it from get player level

06:43.240 --> 06:50.230
to get player level implementation, there's the header file and here is the CPP file.

06:50.260 --> 06:53.050
So that takes care of those errors.

06:53.050 --> 06:56.800
But if we compile again now we get other errors.

06:56.800 --> 07:00.010
So how are we going to fix this?

07:00.040 --> 07:05.530
Well, what I'm going to do is do a project wide search for get player level and I'm going to see everywhere

07:05.530 --> 07:06.790
where we're using it.

07:06.970 --> 07:12.190
The first place I see is the MSI, Max Mana, so I'm going to start there.

07:12.190 --> 07:19.090
We're calling it right here After casting to the combat interface, instead of casting our spec, get

07:19.090 --> 07:22.480
context, get source object to the combat interface.

07:22.480 --> 07:27.160
What I'd rather do is check to see if it implements the interface and set player level.

07:27.160 --> 07:31.690
And if it doesn't, player level can be set to say one.

07:31.690 --> 07:33.250
So I'm going to do this.

07:33.250 --> 07:41.980
I'm going to create a local non-const int 32 called player level and set it equal to one and remove

07:41.980 --> 07:49.660
that line where I've declared that const int 32 and instead of casting to I combat interface, I'm going

07:49.660 --> 07:51.370
to use an if statement.

07:51.370 --> 07:59.500
And in the if statement I'm going to take this source object spec, get context dot get source object

07:59.590 --> 08:01.480
and I'm going to paste it here.

08:01.480 --> 08:07.810
And on this source object I'm going to call implements and implements allows me to check to see if it

08:07.810 --> 08:09.190
implements the interface.

08:09.280 --> 08:10.570
But very important.

08:10.570 --> 08:15.760
We have to remember that if we call implements, we use the U version and not the I version.

08:15.760 --> 08:21.340
So we use U combat interface for this implements function call.

08:21.340 --> 08:28.570
Now, if we make it inside this, then that source object does implement the interface and then we can

08:28.570 --> 08:35.140
set our player level by calling the static execute version on I combat interface.

08:35.140 --> 08:37.210
So it can be a little confusing.

08:37.210 --> 08:42.760
When we're using implements, we use U combat interface, but when we call that function on it, we

08:42.760 --> 08:49.510
use I combat interface and the function is execute, underscore, get player level and when we call

08:49.510 --> 08:50.680
the execute version.

08:50.780 --> 08:54.090
Since it's static, it needs to know which object we're calling it on.

08:54.110 --> 08:56.660
That's going to be this source object.

08:56.660 --> 08:59.150
So we're going to pass that in as well.

08:59.330 --> 09:03.340
And now we don't need this cast to eye combat interface.

09:03.350 --> 09:05.450
We're going to go ahead and remove that.

09:05.450 --> 09:07.910
And now we have just this line here.

09:07.970 --> 09:16.730
So these lines where I create the player level and set them, we can use these instead in MK Maxhealth

09:16.730 --> 09:17.320
as well.

09:17.330 --> 09:26.570
So I'm going to copy them and go into my MKs folder into MK Maxhealth and replace these two lines here

09:26.570 --> 09:30.770
where we're casting to the interface and creating this player level local variable.

09:30.860 --> 09:37.220
I'm going to replace them with this function call where we check to see if the source object implements

09:37.220 --> 09:41.050
the interface and then we call the execute version of get player level.

09:41.060 --> 09:46.100
So that's going to take care of the usage of get player level in the MKs.

09:46.100 --> 09:48.140
I'm going to go ahead and close out of those.

09:48.140 --> 09:53.610
We're done with those and I'm going to search project wide for get player level again and see where

09:53.610 --> 09:54.930
else we're calling it.

09:54.990 --> 09:59.100
I see that I'm using it in the attribute set, but we'll get to that.

09:59.100 --> 10:07.080
I'd like to skip to the exec calc as we're using get player level a couple times here when we're accessing

10:07.080 --> 10:12.290
the player level to calculate our armor penetration coefficient and so on.

10:12.300 --> 10:19.680
Now what I'd like to do is scroll up to the top because we're storing pointers to the combat interface.

10:19.710 --> 10:26.460
What I'd rather do instead is just calculate the player level and have a couple local integers for player

10:26.460 --> 10:30.210
level instead of just calling get player level.

10:30.210 --> 10:36.030
In multiple instances here, the way we did it is totally fine, but I'd like to do it a little bit

10:36.030 --> 10:36.660
differently.

10:36.660 --> 10:42.690
So what I'm going to do is first check our source avatar and see if it implements the interface.

10:42.690 --> 10:44.130
So we're going to say if.

10:44.130 --> 10:45.630
Source Avatar.

10:46.250 --> 10:49.730
Implements you combat interface.

10:51.200 --> 10:54.350
And if it does, we can get the player level for it.

10:54.440 --> 10:59.930
So I'm going to make a local int 32 called source player level.

11:00.590 --> 11:08.420
I'm going to start it off at one without a dot f and now I'm realizing, I think I went into my mix

11:08.420 --> 11:11.540
and I made an int 32 with dot f.

11:11.540 --> 11:12.920
That's just habit.

11:12.920 --> 11:17.420
I tend to put a dot f after a lot of things, so it's an n 32.

11:17.450 --> 11:20.720
We don't need a dot f there that would result in an implicit conversion.

11:20.960 --> 11:23.440
So I'm going to remove those from there.

11:23.480 --> 11:30.020
A little tangent there, but I'm going to have this source player level and I'm going to take my source

11:30.020 --> 11:37.520
player level here and set it equal to eye combat interface, execute get player level passing in source

11:37.520 --> 11:38.360
Avatar.

11:38.360 --> 11:40.340
So I'd rather do it this way.

11:40.340 --> 11:44.960
And now we have just an int 32 for source player level.

11:45.140 --> 11:49.290
I'm going to have the same thing for the target as well.

11:49.290 --> 11:55.950
So we're going to have a target player level and this will be checking if the target Avatar implements

11:55.950 --> 11:56.760
the interface.

11:56.760 --> 12:03.810
And if it does, then we're going to set target player level equal to get player level passing in Target

12:03.810 --> 12:04.620
Avatar.

12:04.620 --> 12:10.530
Now that we have source player level and target player level, we can delete these pointers as we don't

12:10.530 --> 12:15.210
really need them anymore and we can find where we're calling get player level here.

12:15.450 --> 12:21.990
So instead of calling source combat interface, get player level here, we can just pass in source player

12:21.990 --> 12:22.590
level.

12:23.140 --> 12:27.550
And right here we're calling target Combat interface, get player level.

12:27.550 --> 12:28.150
Instead.

12:28.150 --> 12:33.430
I'm going to pass in target player level and I believe we're using it again here.

12:33.640 --> 12:40.210
I'm going to replace this function call with target player level as well, and that is the only place

12:40.210 --> 12:41.230
we use that interface.

12:41.230 --> 12:44.530
So now we don't need to cast to the interface.

12:44.530 --> 12:47.440
We're just checking to see if we implement the interface.

12:47.590 --> 12:48.130
Okay.

12:48.130 --> 12:54.910
So searching project wide again, I see that aura attribute set looks like just about the last place

12:54.910 --> 12:56.860
we're calling get player level.

12:56.860 --> 12:57.910
Actually almost.

12:57.910 --> 13:00.880
We're calling it an aura ability system library as well.

13:00.880 --> 13:04.870
So first the attribute set and send Exp event.

13:04.900 --> 13:08.830
So here we don't have to cast to the combat interface.

13:08.830 --> 13:13.870
Instead, we're going to take props dot target character and see if it implements the interface.

13:13.870 --> 13:16.180
So we'll replace the conditional here with props.

13:16.180 --> 13:19.690
Dot target character call implements.

13:21.660 --> 13:24.150
You combat interface.

13:24.150 --> 13:32.730
And if it does, then here we'll call that function I combat interface execute get player level passing

13:32.730 --> 13:34.830
in props dot target character.

13:37.580 --> 13:44.570
So now these two local variables are obtained in consistent manners and it looks a little bit more professional.

13:44.900 --> 13:45.320
All right.

13:45.320 --> 13:50.710
So the last place I think we're using this is in our Ability system library.

13:50.720 --> 13:56.150
Let's go to that and that's here and give startup abilities where again, we're casting to the combat

13:56.150 --> 13:59.840
interface, but instead I think we can just check.

13:59.840 --> 14:05.270
So we're going to take our ASCs get Avatar actor function call.

14:05.300 --> 14:10.700
We'll replace this conditional with get Avatar actor calling implements.

14:10.820 --> 14:13.400
We'll check you combat interface.

14:13.400 --> 14:19.940
And then instead of calling get player level on the combat interface directly, we'll call eye combat

14:19.940 --> 14:26.630
interface, execute get player level and pass in ASC, get Avatar actor.

14:26.750 --> 14:30.530
So that's going to replace our cast there.

14:30.830 --> 14:37.740
Now I'm going to quickly just give this a compile and see what we get and we get a linker error.

14:37.770 --> 14:44.190
Okay, So here's one that might have tripped you up if you tried to compile and do this.

14:44.190 --> 14:50.250
Refactor yourself a linker error that says that a function is already defined.

14:50.280 --> 14:56.250
This can be a little bit tricky, but it all comes back to this get player level function and how it

14:56.250 --> 14:57.300
used to be.

14:57.330 --> 14:59.280
Not a blueprint native event.

14:59.310 --> 15:00.420
Now it is one.

15:00.420 --> 15:07.020
So if we go to combat interface here, now we have get player level right and when we have this get

15:07.020 --> 15:15.450
player level function as blueprint native event, we have to go into the CPP file here as we've defined

15:15.450 --> 15:17.730
a function definition for it.

15:17.760 --> 15:24.000
We can't do this because this get player level definition will be auto generated by the unreal header

15:24.000 --> 15:24.480
tool.

15:24.480 --> 15:31.410
So the unreal header tool sees that blueprint native event macro specifier and generates the implementation

15:31.410 --> 15:32.880
for get player level.

15:33.000 --> 15:38.940
For that reason, this get player level is a redefinition which is not okay.

15:38.940 --> 15:43.320
So for that reason we have to remove this function call altogether.

15:44.040 --> 15:48.600
If we do that, we should be able to compile just fine and we do.

15:49.260 --> 15:49.680
Okay.

15:49.680 --> 15:51.900
So that was a pretty good refactor.

15:51.900 --> 15:56.040
We can do a quick play test and make sure that everything works okay.

15:56.430 --> 15:59.610
Everything should still work the same as it did before.

15:59.700 --> 16:03.390
We've just kind of cleaned up our code so it's a little bit more consistent.

16:07.570 --> 16:13.600
So really quickly, I'm just going to press play caused some damage, make sure that things are working.

16:13.600 --> 16:15.460
I can even take some damage.

16:15.460 --> 16:17.320
And we see that things are working.

16:17.320 --> 16:19.180
Everything works just fine.

16:20.050 --> 16:23.050
We'll do a really quick multiplayer test as well.

16:25.740 --> 16:28.890
Take some damage, caused some damage.

16:30.480 --> 16:32.010
And things are working.

16:34.150 --> 16:37.180
And I'll play his client as well just to try.

16:39.550 --> 16:41.170
Dedicated server setup.

16:43.460 --> 16:44.300
Perfect.

16:44.570 --> 16:45.140
Okay.

16:45.140 --> 16:52.130
So with that, we've just done a little bit of a refactor and we're ready to continue implementing level

16:52.130 --> 16:56.360
up capabilities and we'll level up in the next video.

16:56.780 --> 16:59.420
Excellent job and I'll see you soon.
