WEBVTT

00:07.070 --> 00:11.300
Okay, so we have our character class info data asset.

00:11.420 --> 00:13.920
The question is where do we store this?

00:13.940 --> 00:18.950
Now we can have one on every character, but that seems a little bit redundant.

00:18.980 --> 00:22.670
Every character doesn't need the data asset, right?

00:22.970 --> 00:25.610
We can store it in one central location.

00:25.640 --> 00:33.350
Now the game mode is a possibility as the game mode is where the rules of the game are determined.

00:33.350 --> 00:40.760
And in a sense, this data asset contains a lot of rules, such as what attribute values enemies should

00:40.760 --> 00:42.290
have based on their level.

00:42.500 --> 00:49.120
So we could store a data asset on the game mode, and that way it's only on a single class.

00:49.130 --> 00:54.430
We're only taking up memory for a single one of these data assets.

00:54.440 --> 01:01.670
So what I'd like to do is store the data asset on the game mode and be able to set this from Blueprint.

01:02.060 --> 01:06.960
So we're going to forward declare this data asset type.

01:06.960 --> 01:13.260
It's a you character class info and we're going to have a variable of that type.

01:13.260 --> 01:21.030
This can be a T object pointer of type U character class info called character class info.

01:21.150 --> 01:23.490
And this can be a u property.

01:23.490 --> 01:28.920
We can set its value with edit defaults only we can give it a category.

01:30.220 --> 01:33.580
And this will be character class defaults.

01:33.850 --> 01:38.650
And if we want to access it from outside of the class, we can make it public.

01:38.650 --> 01:41.520
So now our game mode will have this.

01:41.530 --> 01:48.100
And the question is, how are we going to access it and apply this information to our enemies?

01:48.100 --> 01:54.580
Well, we have a blueprint library, which is a pretty good spot for these types of functions, ways

01:54.580 --> 01:57.900
to apply the default information.

01:57.910 --> 02:06.130
If I go to my ability system folder and open aura ability system library, we can get the H file and

02:06.130 --> 02:07.750
the CPP file open.

02:07.750 --> 02:09.730
We can make a function for this.

02:09.730 --> 02:11.490
So I'm going to make one.

02:11.500 --> 02:13.360
It's going to be a void function.

02:13.360 --> 02:15.760
I'll go ahead and make it static as well.

02:15.850 --> 02:25.690
And this is going to be a function that will initialize default attributes based on a character class

02:25.690 --> 02:27.160
and the level.

02:27.340 --> 02:31.790
So we're going to call this initialize default attributes.

02:33.060 --> 02:41.880
It's going to take in an E character class called character class, and we'll say a float called level.

02:42.030 --> 02:46.470
Now, character class is not really defined here, right?

02:46.470 --> 02:50.910
This is not a defined enum that's defined in character class info.

02:50.910 --> 02:54.320
So we can just include character class info in here.

02:54.330 --> 02:55.470
That's in data.

02:55.470 --> 03:04.050
So we're going to say right here, let's include include data slash character class info.

03:04.140 --> 03:07.020
Now this can be a blueprint callable function.

03:07.020 --> 03:09.420
It's in a blueprint library after all.

03:09.630 --> 03:16.620
So I'm going to make it blueprint callable and it's going to be in category or a ability system, library

03:16.650 --> 03:20.280
slash character class defaults.

03:20.460 --> 03:22.680
So let's make a function definition.

03:23.290 --> 03:30.370
Now, what this needs to do is it needs to get the data asset, the character class info, data asset,

03:30.370 --> 03:32.350
and that exists on the game mode.

03:32.590 --> 03:36.400
So I'm going to use you gameplay statics to get the game mode.

03:37.930 --> 03:39.850
So we're going to call get game mode.

03:41.140 --> 03:44.350
Now this requires a world context object.

03:44.530 --> 03:47.680
So that means to initialize default attributes.

03:47.680 --> 03:49.890
We need a world context object.

03:49.900 --> 03:53.710
So let's add that to the function that could be a new object.

03:53.800 --> 03:56.290
And notice our other functions have this input.

03:56.320 --> 04:01.600
We can just copy that and we can paste it here in our new function.

04:01.600 --> 04:07.780
So it's going to take a world context object as the first input now, and that's what we're going to

04:07.780 --> 04:09.460
pass in to get game mode.

04:09.670 --> 04:12.550
Now get game mode returns a game mode base.

04:12.580 --> 04:15.760
We're going to cast this to a game mode base.

04:15.850 --> 04:17.620
So let's cast.

04:20.510 --> 04:23.780
To a ora game mode base.

04:23.780 --> 04:26.580
And only if this cast succeeds.

04:26.600 --> 04:28.550
Should this function continue.

04:28.550 --> 04:30.860
So we'll say a ora game mode.

04:31.290 --> 04:31.760
Base.

04:32.540 --> 04:34.520
We'll call it ora game mode.

04:35.000 --> 04:37.970
We can even call it ora GM if we wanted to.

04:37.970 --> 04:42.560
And we'll just say if ora game mode.

04:43.630 --> 04:44.950
Is null.

04:48.080 --> 04:49.670
Then we'll return.

04:49.970 --> 04:55.430
So once we have the game mode, we can access that data asset character class info.

04:55.460 --> 04:58.670
We can say you character class info.

04:59.300 --> 05:06.890
Class info equals or a game mode character class info.

05:07.550 --> 05:14.180
And now that we have class info, we can access data based on the character class because character

05:14.180 --> 05:16.910
class info has a lookup function.

05:16.910 --> 05:22.820
Get class default info that returns this struct f character class default info.

05:22.970 --> 05:31.130
So here in our ability system library we can create a character class default info called class.

05:32.070 --> 05:41.940
Default info and we can take our class info data asset and we can call get class default info passing

05:41.940 --> 05:43.440
in character class.

05:44.740 --> 05:49.420
Now, there's really no need to have a local variable called class info.

05:49.450 --> 05:57.070
We could just take or a game mode right here and use that so we can say or a game mode character class

05:57.070 --> 06:02.240
info, get class default info and we can remove this local variable here.

06:02.260 --> 06:05.140
Either way we're getting the class default info.

06:05.170 --> 06:11.380
Now we're going to need to apply some gameplay effects that exist in this struct.

06:11.380 --> 06:13.510
And what are we applying those effects to?

06:13.540 --> 06:17.110
Well, we need an ability system component to apply to.

06:17.140 --> 06:21.430
So we're going to need to pass in an ability system component to this function as well.

06:21.760 --> 06:25.240
So let's add that as the last input parameter.

06:25.240 --> 06:33.640
So this will be a new ability system component pointer and we'll go ahead and add a forward declaration

06:33.640 --> 06:34.720
for that type.

06:34.990 --> 06:37.000
And this will just be called ASC.

06:37.390 --> 06:46.750
So we'll add an ASC input parameter here in the CPP file as well after level and that's how we're going

06:46.750 --> 06:49.090
to apply the gameplay effect.

06:49.780 --> 06:56.740
So we need to apply a gameplay effect and we know how to do that with an ability system component,

06:56.740 --> 06:57.100
don't we?

06:57.130 --> 06:59.380
We can apply a gameplay effect spec.

06:59.380 --> 07:09.850
If we take our ASC, we can call apply gameplay effect spec to self, apply gameplay effect spec to

07:09.850 --> 07:10.690
self.

07:10.720 --> 07:13.750
Now I always like to start with this function.

07:13.750 --> 07:18.910
Call and work backwards because the input parameters always remind me of what I need.

07:18.940 --> 07:25.390
This function needs a gameplay effect spec and I know that I can make that using my ability system component

07:25.510 --> 07:29.920
using ASC I can call what's the function name?

07:30.130 --> 07:38.260
Make outgoing spec and make outgoing spec requires a subclass of that's a gameplay effect class and

07:38.260 --> 07:41.320
a level and an effect context handle.

07:41.470 --> 07:44.350
Now we'll worry about that effect class in a second.

07:44.350 --> 07:46.030
Let's make that context handle.

07:46.060 --> 07:47.350
How do I do that?

07:47.380 --> 07:52.670
I use the ability system component and call make effect context.

07:53.060 --> 07:56.780
So pretty simple so I can pass that in.

07:57.470 --> 07:59.570
So really, I don't need to call it here.

07:59.570 --> 08:03.440
I can just call it directly in the function call here.

08:03.440 --> 08:09.140
But first I need a gameplay effect, so I'm going to go ahead and just remove that line.

08:09.140 --> 08:11.840
And what's the gameplay effect?

08:11.870 --> 08:14.120
Well, we have class default info.

08:14.150 --> 08:16.580
If I just type class default info.

08:16.610 --> 08:24.560
This is that structure and in the struct we have a primary attributes to subclass of gameplay effect

08:24.740 --> 08:26.360
so we can apply that.

08:26.360 --> 08:26.960
Right.

08:26.960 --> 08:34.250
So if I hit enter then I have access to this subclass of I'm going to go ahead and control X and control

08:34.250 --> 08:37.220
V to place it right into the function call.

08:37.670 --> 08:39.560
And I can also pass in a level.

08:39.560 --> 08:42.260
Now this function takes a level as an input.

08:42.260 --> 08:47.270
I can pass that in and then we need our gameplay effect context handle.

08:47.270 --> 08:56.150
I can simply call ASC, make effect context and this returns me an gameplay effect spec handle so I

08:56.150 --> 09:03.470
can make an f gameplay effect spec handle called and I'll call this primary attributes.

09:04.140 --> 09:05.790
Spec handle.

09:06.440 --> 09:13.960
And we'll pass this in to apply gameplay effects back to self and of course apply gameplay effects.

09:13.970 --> 09:18.350
Back to self requires an effect spec, not a spec handle.

09:18.350 --> 09:26.420
So we take the handle, we get the data we call, get on that and dereference it with the star and we've

09:26.420 --> 09:27.260
got it.

09:27.260 --> 09:33.020
And of course this could be a const spec handle so we can make it a const spec handle.

09:33.440 --> 09:34.220
Okay.

09:34.220 --> 09:41.960
So now we have a function that can take a context object, a character class enum constant, a level

09:41.960 --> 09:48.650
and an ability system component, and it'll get the data asset from the game mode called class default

09:48.650 --> 09:49.100
info.

09:49.100 --> 09:51.830
And from that get the primary attributes.

09:51.860 --> 09:58.460
Now, after we've applied the gameplay effect for primary attributes, we can apply the secondary and

09:58.460 --> 10:00.800
the vital gameplay effects as well.

10:01.100 --> 10:03.920
Now that's going to be relatively easy.

10:03.950 --> 10:06.360
We're just going to do the same thing again.

10:06.360 --> 10:15.720
We're going to make a new spec handle and apply that only this one will call this secondary attribute

10:15.720 --> 10:16.800
spec handle.

10:16.830 --> 10:23.310
We'll call make outgoing spec, but instead of the primary attributes class, we're going to get the

10:23.310 --> 10:30.420
secondary and that's actually not even in the class default info struct that's going to be in effect

10:30.420 --> 10:31.380
that we have.

10:31.410 --> 10:33.090
That's common among all enemies.

10:33.090 --> 10:40.230
If we go back to character class info, it's not in our map of structs, it's just on the data asset

10:40.230 --> 10:40.710
itself.

10:40.710 --> 10:42.690
It's called secondary attributes.

10:42.930 --> 10:45.120
So we can access that directly.

10:45.120 --> 10:52.440
And in this case, we're going to either take the game mode and get the class info and access that class

10:52.440 --> 10:59.490
right there, or we can go back to having a local variable of type U character class info pointer.

10:59.490 --> 11:05.640
We can call this, I'll just call it character class info and that's going to be from our game mode

11:06.510 --> 11:12.660
character class info, and then we can use that right here instead of going through the game mode.

11:12.660 --> 11:21.780
And we can use that here in our make outgoing spec character class info, accessing secondary attributes

11:21.960 --> 11:28.680
and we can pass in the level and when we apply the effect spec, we have to use the secondary attribute

11:28.680 --> 11:33.030
spec handle instead of the primary and we'll do the same thing for Vital.

11:33.030 --> 11:39.570
So we're going to copy these two lines, paste them here and we'll call this one Vital attributes spec

11:39.570 --> 11:40.260
handle.

11:40.440 --> 11:47.640
And this will be making an outgoing spec from character class info vital attributes.

11:47.850 --> 11:53.190
And then when we apply it, we're going to apply vital instead of secondary.

11:53.190 --> 11:59.100
And now we're applying all three gameplay effects with our initialized default attributes.

11:59.400 --> 12:04.140
So all of this requires that the game mode have the data asset on it.

12:04.260 --> 12:10.080
Otherwise we'll be attempting to access a null pointer and we'll get a crash here.

12:10.080 --> 12:17.100
So if we get a crash on the lines in this function, particularly line 50, we know it's because character

12:17.100 --> 12:18.870
class info is null.

12:19.050 --> 12:25.950
So once we have that set on the game mode, we can actually call initialize default attributes to initialize

12:25.950 --> 12:32.490
the attributes of our enemies provided that our enemies have a valid level and a character class.

12:32.490 --> 12:34.950
Now we haven't added a character class to the enemy.

12:34.980 --> 12:39.960
Why don't we go ahead and do that so we can go into character and enemy.

12:39.960 --> 12:41.880
This should be in the public folder.

12:41.880 --> 12:47.580
So public character or an enemy and we can add a character class.

12:47.670 --> 12:52.230
Now if we're going to use the character class enum, we should include the header here.

12:52.980 --> 13:02.610
Include and that's going to be in ability system slash data slash character class info dot h and we'll

13:02.610 --> 13:09.480
put this down here by the level and then this should be edit anywhere blueprint read only category character

13:09.480 --> 13:10.410
class default.

13:10.410 --> 13:16.050
So we'll go ahead and just copy level and instead of level we're going to have an E character class

13:16.050 --> 13:18.720
called character class.

13:18.990 --> 13:26.070
We can give it a default value of E character class and we can choose one, say warrior, but this will

13:26.070 --> 13:28.800
be editable from the blueprint.

13:28.980 --> 13:34.890
And now that we have a character class, we can initialize our default attributes.

13:34.890 --> 13:36.480
So let's do that.

13:36.480 --> 13:45.510
We'll go to Aura Enemy CP and we can do this after our ability actor info has been initialized because

13:45.510 --> 13:51.000
at that point we know that our ability system component has had its Avatar actor set.

13:51.000 --> 13:57.460
And in fact what we're doing is calling initialize default attributes here, which is defined on the

13:57.460 --> 13:58.510
base class.

13:59.000 --> 14:03.500
So we can actually override this by making it a virtual function.

14:03.500 --> 14:08.830
Let's go to or character base and see what that function looks like.

14:08.840 --> 14:09.560
Here it is.

14:09.560 --> 14:11.690
Initialize default attributes.

14:11.690 --> 14:19.610
We can go ahead and make this a virtual function and then we can override it in aura enemy.

14:19.610 --> 14:21.200
So that's a protected function.

14:21.200 --> 14:27.350
So here in Aura Enemy in the protected section, we'll go ahead and override.

14:28.230 --> 14:34.290
Initialize default attributes and we'll generate the definition.

14:34.620 --> 14:39.060
And instead of calling super, we're going to implement this a little differently.

14:39.060 --> 14:43.260
We're going to use the aura ability system library.

14:43.260 --> 14:52.080
So your aura ability system, library and using that, we have to have the header included and we're

14:52.080 --> 14:56.430
going to call initialize default attributes.

14:56.430 --> 14:58.800
And this needs a world context object.

14:58.800 --> 15:00.120
We'll pass in this.

15:00.150 --> 15:01.710
It needs a character class.

15:01.710 --> 15:03.570
We'll pass in character class.

15:03.600 --> 15:09.390
It needs a level we'll pass in level and it needs an ability system component.

15:09.420 --> 15:11.910
We'll pass in ability system component.

15:12.710 --> 15:20.000
So now we're initializing the default attributes using our new function that we added to our ability

15:20.000 --> 15:27.380
system library that gets the data asset stored on the game mode, gets the class information based on

15:27.380 --> 15:34.880
our character's class, and applies a gameplay effect at the level passed in and we're passing in our

15:34.880 --> 15:37.940
character's level here on the enemy class.

15:37.940 --> 15:45.020
So now we should see our enemies come in with the correct stats based on their class and their level.

15:45.230 --> 15:47.750
So why don't we go ahead and run this in debug mode?

15:47.750 --> 15:53.660
We've done quite a few changes here, so let's first make sure this compiles and we can launch.

15:54.640 --> 16:00.280
So with a successful compile, we now need to set the data asset on our game mode.

16:01.870 --> 16:03.760
Okay, I'm going to go ahead and click open.

16:03.760 --> 16:06.330
Looks like we have quite a few assets open.

16:06.340 --> 16:13.810
I think I can close out of my curve tables, I can close out of my gameplay effects and I can close

16:13.810 --> 16:14.770
out of my character.

16:14.770 --> 16:15.790
Class info.

16:15.790 --> 16:21.910
Let's go into blueprints game or a game mode and here's the character class info.

16:21.940 --> 16:27.040
We can set the data asset, character class info, compile and save.

16:27.040 --> 16:33.760
And now we should see that our characters will be initialized with their attributes if we press play.

16:33.790 --> 16:39.100
Oh, and we have a exception here in Maxhealth.

16:39.100 --> 16:40.240
Interesting.

16:40.240 --> 16:46.810
And it looks like we're making the same mistake that we have made before, which is we're applying gameplay

16:46.810 --> 16:49.390
effects without setting the source object.

16:49.420 --> 16:57.040
Because as soon as a gameplay effect uses this mod mag, calc, this MSI, it's going to get the combat

16:57.040 --> 16:59.500
interface from the source object.

16:59.530 --> 17:00.550
And that's okay.

17:00.550 --> 17:03.140
It's easy to forget these little details.

17:03.140 --> 17:09.920
So what I can do is I can hit stop and we can make sure that we're adding a source object from which

17:09.920 --> 17:12.020
to get that combat interface.

17:12.050 --> 17:18.800
Now we can do that here before we apply these gameplay effects and we can get that source object from

17:18.800 --> 17:23.780
the ability system component because the ability system component has the avatar.

17:23.870 --> 17:34.040
So we're going to say a actor avatar actor equals and we'll get the ASC and call get Avatar actor.

17:34.070 --> 17:37.700
That can be the source for these gameplay effects.

17:37.700 --> 17:40.580
So how do we set the source object?

17:40.580 --> 17:43.640
Well, we set that on the effect context, don't we?

17:43.640 --> 17:49.040
So we're going to take the ASC effect context and actually make a local variable now.

17:49.130 --> 17:55.370
So I'm going to say make effect context, which returns a gameplay effect context handle.

17:55.580 --> 17:56.840
So we'll make one.

17:57.730 --> 17:59.500
F gameplay effect.

18:00.400 --> 18:01.660
Context.

18:03.130 --> 18:08.800
Handle and we'll call this one primary attributes context handle.

18:09.100 --> 18:16.930
And after making it, we take it primary attributes, context, handle and call add source object.

18:16.930 --> 18:20.680
And we're going to pass in Avatar actor for that.

18:20.710 --> 18:25.810
Now we're going to use this as the context handle instead of just calling make.

18:25.840 --> 18:28.120
Effect context right here.

18:28.270 --> 18:30.400
And we're going to do the same thing.

18:31.450 --> 18:36.760
Making a new context handle and setting the source object for the secondary.

18:36.790 --> 18:37.240
Effect.

18:37.240 --> 18:40.330
So we're going to call this one secondary.

18:41.330 --> 18:43.340
Attributes context handle.

18:43.820 --> 18:48.890
We're going to add the source object to that and use that as the context handle.

18:49.100 --> 18:52.280
And we'll do the same thing for the vital attributes.

18:52.490 --> 18:55.190
So once again, making an effect context.

18:55.190 --> 19:00.770
This one will be called vital attributes context handle, adding the source, object to it and then

19:00.800 --> 19:03.880
using it here in make outgoing spec.

19:03.890 --> 19:08.560
And now we have source objects for all of these effects.

19:08.570 --> 19:12.980
So once again, let's compile and launch and run a debug mode here.

19:14.800 --> 19:16.330
Okay, so we're back open.

19:16.330 --> 19:21.430
I'm going to get that game mode back open, make sure it has character class info, which it does,

19:21.430 --> 19:23.410
and we'll go ahead and press play.

19:23.860 --> 19:24.340
All right.

19:24.340 --> 19:27.220
So we have our game working here.

19:30.070 --> 19:36.670
And of course we can cause damage to the enemies, but it's a little bit hard to tell whether or not

19:36.670 --> 19:40.930
these enemies have their default attributes set to the correct values.

19:40.930 --> 19:46.240
And that's because, well, we don't really have a way to see those values unless we just print a bunch

19:46.240 --> 19:47.700
of debug messages.

19:47.710 --> 19:49.690
So that makes it a little hard.

19:49.810 --> 19:53.320
But of course we have the ability to run in debug mode.

19:53.350 --> 19:59.410
So we could place a breakpoint here for example, and just play the game and we can see what the world

19:59.410 --> 20:00.840
context object is.

20:00.850 --> 20:03.040
Looks like it's the goblin slingshot.

20:03.070 --> 20:08.530
We can check out the value for the character class while it's warrior because we haven't set that to

20:08.530 --> 20:14.890
Ranger, but we can always check out that spec handle for the primary attribute spec handle.

20:14.890 --> 20:16.450
We can check that out.

20:16.480 --> 20:23.020
We can look at its data and see that its definition here is primary attributes warrior.

20:23.020 --> 20:27.520
We can look at the modifiers and we can see that here's the strength modifier.

20:27.520 --> 20:32.360
We can see the modifier op, the modifier magnitude calculation type.

20:32.360 --> 20:35.180
Here we can see the scalable float magnitude.

20:35.180 --> 20:41.870
We can see that its value is one, but that's because we're scaling that value by the curve and we can

20:41.870 --> 20:50.090
see the curve table, we can see that we're indexing the strength row name from the table and so on.

20:50.090 --> 20:55.850
So we can see that we're actually accessing all the correct data here.

20:55.850 --> 21:02.570
And if we wanted to go out of our way, we could print some debug messages and see those values.

21:02.570 --> 21:05.180
But for now I trust it.

21:05.180 --> 21:13.340
So I know that we're actually initializing our enemy values and we can go to our slingshot enemies here

21:13.400 --> 21:19.220
and select them and take their details, panels and search for class defaults.

21:19.820 --> 21:24.790
And we can see that here's their level and here's their character class and we can change that.

21:24.800 --> 21:27.620
I'm going to change these guys to Ranger.

21:27.620 --> 21:35.720
So both of these will be Rangers at level one and they'll be initialized with the Ranger gameplay effect

21:35.720 --> 21:38.810
and we can at least make sure that's happening.

21:38.960 --> 21:45.470
We can have that breakpoint here and we can make sure the character class is Ranger, which it is.

21:45.500 --> 21:51.980
Of course, we have three enemies being initialized here, so this could have easily been the warrior

21:51.980 --> 21:57.620
and I could just click resume and then we'd get this for the next enemy being initialized.

21:57.620 --> 22:03.620
But this is one of the slingshot enemies, and I can see that the primary attribute spec handle has

22:03.620 --> 22:05.000
a valid value.

22:05.000 --> 22:12.410
It has data, we can expand def, we can look at modifiers and we see that we're modifying the four

22:12.410 --> 22:14.330
primary attributes.

22:14.360 --> 22:22.820
We have modifier magnitude with scalable float using the primary attributes Ranger So it's using the

22:22.820 --> 22:24.530
correct curve table.

22:24.740 --> 22:26.960
And that was all I wanted to see.

22:26.990 --> 22:28.040
Perfect.

22:28.070 --> 22:28.730
All right.

22:28.730 --> 22:34.190
So now that we're initializing attributes for the enemies, we can start worrying about causing damage

22:34.190 --> 22:35.240
to the enemies.

22:35.240 --> 22:38.550
And that's what we'll be concerned with in the videos to come.

22:38.570 --> 22:40.700
Great job and I'll see you soon.
