Welcome back.
Now, in our character class info, we have a set by color version of our primary attributes gameplay
effect.
We now need to initialize our attributes if we're loading in from disk.
I'd like to create an Aura Ability system library function for this.
So I'm going to go into Aura Ability System library.
I'm looking at initialize default attributes as a sort of example, but I'm going to make a new version
of this that we can pass in data that we're loading in from disk.
So I'm going to find here in the header file initialize default attributes.
Because I'd like to have a new version of this function right beneath it.
So this can be static.
It can be void.
We're going to call this initialize default attributes from save data.
And this is going to take in a world context object, but it doesn't need the character class.
It doesn't need the level either.
Now we are going to pass in the ability system component, but on top of that we're going to pass in
save data.
We can pass in you load screen save game and add a forward declaration for it.
And we'll call this save game.
So with that let's generate this function definition.
Now what are we going to do here.
It's going to be similar to initialize default attributes.
For one we do want to get character class info.
And we're doing that on this line in initialize default attributes.
So we're going to get that character class info here.
And if this character class info is a null pointer.
In other words we're on a client I'm going to return.
So if character class info equals null pointer return.
Now what I'm going to do is from character class info I'm going to get those primary attributes, the
set by caller version.
And I'm going to assign set by caller magnitudes to it.
Now we have an example of that.
If we scroll almost all the way down to the bottom we have apply damage effect right.
And apply damage effect is assigning set by caller magnitudes to the spec handle.
And what it needed was a few things.
It needs to get the gameplay tags.
It needs the source avatar actor so we can add a source object.
It needs to make an effect context handle, and it needs to make a spec handle and assign some set by
caller magnitudes.
So I'm going to take the first line where we're assigning a set by caller magnitude all the way up to
the gameplay tags and copy it.
Now I'm going to remove most of this, but it does have several things that I need.
So I'll go back up to my new function, initialize default attributes from Save Data and paste this
here, and we'll start to take out what we don't need.
First of all, we need gameplay tags.
That's a good one for the source avatar actor.
We already have the ability system component, so we can call get avatar actor directly from that for
the effect context handle.
Again, we already have the ability system component we can simply call make effect context on it.
We can add the source object using source avatar actor.
That's fine, but we don't need to set death, impulse, knockback, force, or any of these damage
related variables on the context handle.
Let's go ahead and remove those lines.
Now we do need a spec handle.
But again we have the ability system component.
We can call make outgoing spec directly on it.
And for the damage gameplay effect class.
Now we're going to need to get that class from character class info.
So we're going to use character class.
Sorry, autocomplete gave me something I didn't want.
I'm going to have to take that out.
What I meant to type was character class info.
And from that we're going to get primary attributes set by color.
And we're going to apply this effect at level one.
So one dot f and we're going to pass in the effect context handle.
So now that we have a spec handle we can start assigning set by color magnitudes for our primary attributes.
And those exist on our save game object.
So we're calling assign tag set by color magnitude passing in the spec handle.
But the tag now is going to be an attribute tag.
We're going to use gameplay tags dot attributes primary.
And we can start with strength.
And for the value we're going to get them from saved game.
We're going to get save game strength.
And now we've set our primary attribute set by color magnitude for strength.
And we're going to do this three more times 123.
And we're going to change the attribute tag for each of them.
For the second one we'll do intelligence and use save game intelligence.
For the third one, we'll do resilience and set it to save game resilience.
And finally we have vigor using save game vigor.
Now that we've set these we can apply that gameplay effect.
And we're doing that all the way down here as well.
If we go back down, we'll see that we take the ability system component and call apply gameplay effects
back to self.
That's it.
So let's go back up to initialize default attributes from save data.
I just searched for save game and that took me to it.
So we're going to apply this effect.
We're going to take the AC and call apply gameplay effect spec to self.
We're going to apply that gameplay effect spec.
So we're going to take spec handle.
Dot data.
We're going to dereference data now.
After this we can apply the secondary attributes.
And we can do that the same way we did it up here in initialize default attributes.
We just have to get character class info, secondary attributes and character class info, vital attributes.
But we have to be careful here because the secondary attributes in character class info, that's the
secondary attributes for enemies, isn't it?
Because it doesn't have infinite duration, it's an instant effect.
So that's something we have to keep in mind.
We're going to want infinite duration gameplay effects for these.
So first let's actually apply what we've got here.
We'll go ahead and paste these lines down here and replace avatar actor with source.
Avatar actor.
And replace level with one dot f.
But we're going to need character class info to have infinite duration, secondary and vital attributes
gameplay effects.
So that's something we are going to need to fix.
So our character class info data asset is going to need a different secondary attributes.
It's going to need one that's infinite duration so that we can apply this to aura.
The vital attributes is actually just an instant gameplay effect applied after secondary attributes
have been applied, but secondary has to be an infinite one.
So let's go into character class info and add one more T subclass of.
For secondary attributes.
We'll copy the secondary attributes one and we'll call this secondary attributes underscore infinite.
And this one will have for aura specifically.
And that's the one that we're going to use here in Aura Ability System library.
We'll use secondary attributes infinite.
And we'll just set that in the data asset to that gameplay effect.
That we already have in aura.
Okay.
So now that we have this, if we remember to set that secondary attributes infinite, we can initialize
default attributes from save data in aura character right here.
If this is not the first time loading in.
So we can use you aura ability system library.
Initialize default attributes from save data passing in a world context object.
That's this.
And passing in an ability system component.
And finally passing in a u load screen save game.
That's going to be the save data that we have here.
Save data.
And with that we should be loading in our attributes once we've saved them.
Now that's a big thing to test, so let's compile.
Make sure to add that infinite gameplay effect to class default info and see if we can save some progress.
We have lots of things to test.
We can check our level, our XP spell points, attribute points, even our primary attributes after
spending points.
So let's test it out okay, so I'm going to open up my assets and get a character class info.
Here's secondary attributes infinite.
And that's going to be our GE secondary attributes not the enemy not test just secondary attributes.
That's the infinite one.
If we browse to it and open it up we'll see that it has.
Infinite duration.
That's the one we'll use.
All right, we're ready to test.
Let's go into maps load menu.
And I'd like to clear out our saved data.
So I'm going to select this slot and delete it and just make a new one.
I'll give it my proper name.
Select that slot and press play.
Now it looks like I sort of leveled up.
Kind of.
It started me off at level zero.
So that's weird.
We have zero spell points, but we have our attributes initialized.
Now, I know why I started off at level zero, because if I go back to here, we're setting our level
to the save data player level, which is zero really.
We should be setting these to default values.
If this is our first time loading in.
If not then we should load them in from disk, right?
So if it's our first time loading in we should move these lines.
Right here.
We should move them.
The whole thing, the if statement and all.
I'm going to control X to cut it and move it into this else here because otherwise it's our first time
loading in.
And we should start off at our default values, which means we don't have to load them up at all.
Our player state already has default values.
It starts our level off at one.
Our XP is one.
Strangely, it should actually be zero.
We should start off with empty XP and no attribute points.
No spell points.
So that's great.
So that's going to fix that issue.
We don't need to load anything in if it's the first time right.
So we're ready to test this.
Although one last thing we're calling load progress which is adding character abilities.
So we don't actually need to add character abilities right here.
We do still want to load character abilities from disk, but we're going to move this to do now into
here where we're loading progress.
So if it's the else case right here, this is where we're loading in.
Not for the first time.
That's when we need to load in abilities from disk.
We don't need to do it here.
And we don't need to call add character abilities.
We're already doing that if it's our first time.
So let's go ahead and test out everything that we've done so far.
Okay.
So what we're going to do is we're going to bring in an enemy so we can level up.
Let's go to blueprints character and we'll bring in a goblin spear.
That way we can gain some experience and test out leveling up.
So let's go to load menu.
And I'm just going to delete all my slots and start fresh I'll call this Steven get that new slot.
Press play and we can try leveling up.
Let's go ahead and kill this enemy.
Okay, so we've leveled up.
We've reached level four.
We now have some spell points.
We have three attribute points.
Three spell points.
Now it doesn't matter what levels our abilities are, we're not loading those in from disk.
So that doesn't really matter.
We could spend a point just to save it and see if we load it in at two.
We just need to remember how many we have.
We'll have two spell points.
I'll go ahead and spend an attribute point on strength.
So that strength is 11 and we'll have two attribute points.
So two and two we'll remember that.
And we're at level four.
So let's go to a checkpoint.
Make sure we save our progress and load back in.
So we're first of all not seeing that my level is four.
So we're going to want to make sure that that level updates.
But we haven't implemented that yet.
Right.
But we can select the slot and load in.
And well I got the level up message and the sound, which is funny.
We don't need that.
So we want to fix that.
But I'm at level four.
I have 11 strength and two attribute points which is what I saved at.
And I have two spell points even though I have no abilities.
And that's because we're not loading in abilities from disk and we're preventing loading those default
abilities if this is not our first time loading in.
So that is the behavior that we expected, because we still need to implement the saving and loading
of abilities.
So that's fine.
But before we get to saving and loading abilities, I just want to take care of a couple little issues.
So the first one is that when I load in, I don't want to see that level up message or hear that level
up sound.
So when we make those broadcasts for when our level changes, we need to broadcast some information,
such as a boolean.
That's true or false depending on if this is loading in that level from disk.
So we still do want to broadcast to our widgets.
So they update themselves, but we don't want the level up effects.
So that's one thing we can do right now.
We can fix that by changing the delegate we broadcast when our level changes.
Now we broadcast that from the player state.
If we go to Aura Player state, we'll see that on level change delegate broadcasts the level.
But when we set the level, we now need to know if we're setting it by loading it in from disk or if
we're actually leveling up.
So that's a fix we can implement.
We can make on level change delegate.
Have a boolean.
That's true if we're leveling up and false if we're not.
So that's going to be an aura player state.
We'll go up to the top.
We have player stat changed and on level change delegate is an on player stat changed.
We're actually going to want a delegate that broadcasts a boolean now instead.
So let's make a multicast delegate to params.
So declare multicast delegate two params.
And this is going to be on level changed.
So we now need to params because the next one is a boolean that will tell us if we leveled up.
So it'll be a B level up and we'll now change.
On level change, delegate to the F on level changed and that means we have to broadcast, right?
Now what we can do is we can make our set level a non-level up setter and just broadcast false here.
That way we don't have to go and change a bunch of code that's setting the level, just really one place.
But we can also have a set level that broadcasts.
True.
And that's for set level with level up.
So let's make another setter just under set level I'll just copy this and this will be set level with
level up.
And actually that means we do need to change where we're calling set level where we're leveling up.
Now this one is going to be just like set level only it's going to broadcast.
True.
So true.
They're now changing.
This delegate is going to break a couple things.
And we're going to have to fix those.
But let's take this step by step.
First let's change where we're calling set level.
When we level up to set level with level up.
And that's going to be in our attribute set when we level up.
So I'm just going to search for set level.
Level up and it looks like we're actually executing interface functions.
We're executing here, adds a player level.
So let's go ahead and go to our character.
And here's add to player level implementation.
So really it looks like add to level is the only function we'd call to level up.
We're never going to set the level directly.
So or a player state add to level could actually just handle broadcasting that delegate.
Let's go to our player state add to level.
And here we'll broadcast true.
So add to level is what we'll use to level up.
And we don't really need set level with level up.
We'll just have set level for setting it when we're loading from disk.
That's totally fine.
So I'm going to remove set level from level up.
Set level with level up.
That is I'm going to remove that.
And we'll just have add to level for leveling up and set level for loading in from disk and in or a
character add to player level will call add to level and when loading from disk we'll call set level
right here and that will broadcast false as our set level will broadcast false in that delegate and
add to level will broadcast true.
So now it's up to our overlay widget controller to accept the broadcast.
Its callback or lambda needs to be compatible with this broadcast.
So the next thing to do is.
Go into the private folder.
Into widget controller, into overlay widget controller.
Here's on level change delegate.
It now needs to take in a boolean.
So bool and this will be called be level up.
And our overlay widget needs to know that too.
So on player level change delegate is a dynamic multicast delegate that needs to take in a boolean now.
So I'm going to go into overlay widget controller dot h.
And we need our on player level changed to now be a delegate that can take a float and a boolean.
So we'll copy on attribute change signature and we'll make one with two params.
And this will be f on level changed signature.
It'll take a float called New Value.
We'll call it new level, actually, and a bool.
And it looks like I don't have the params there.
There we go.
So we have new level and we have a bool called be level up.
And that needs a comma there.
And we'll change the type of on player level change delegate to this type.
Now.
Because of that, it needs to broadcast a boolean as well.
So right here our broadcast will now broadcast be level up.
And now we need to go and fix things that will be broken in blueprint as a result of this.
And I don't think anything else responds to on player level change.
Delegate.
So that's the only thing we need to fix.
Let's compile and launch the editor.
Okay, so player state line 74 we have on rep level.
So this also has to know if it's a level up.
So that's an issue isn't it.
Because on the client we're not going to know.
Although this is a single player loading system here.
So what we're going to do is just pass in true to satisfy that.
And for a multiplayer solution we'd have to think of something else, right?
We'd have to accommodate for that boolean.
But it doesn't matter.
We can just pass in.
True for that and we can compile this now.
So back in the editor we have to go into our overlay widget and fix things.
So let's go to our overlay WB overlay.
Go to the event graph.
And right here is broken.
So we need to refresh the nodes.
And it looks like we broadcast a float.
We can fix that by making this delegate broadcast an integer.
That should have been a float on level change signature.
Can broadcast it in 32.
But that's not a big deal.
However, we are going to want to just really quickly recompile.
If we don't want to have to fix a broken blueprint node again.
So let's recompile and back in the editor we can open our overlay and now we can fix that broken node.
So we'll refresh it.
And we can pass the level end to handle level up message only handle level up message is doing what
it's creating our level up message widget, setting its text and adding it to the viewport.
Right.
We want to prevent all of this if it's not a level up.
So we're going to place a branch here and check level up and only do the level up message if it is a
level up.
Now what about this here value globe level?
Well it looks like there's some weird stuff going on with it, right?
If we control F and search for value globe level, it's right here.
And set widget controllers.
We're setting its widget controller.
It's a separate widget right.
And that means we need to go look at that widget.
If we search for value globe.
We can find it, we can browse to it.
It is in spell globes.
Now we can see what's going on here.
Of course, this is bound to the overlay widget controller's delegate.
Let's refresh this node and we can call set text here.
Now this doesn't matter if it's a level up or not.
So with that we can fix that.
And it looks better.
We can press play and it looks good okay.
So that should fix loading in.
Let's go back and go to load menu.
And let's load in.
And we don't get that level up message even though we're setting our level here to four.
Okay, now don't freak out if you open your attributes menu and try to spend a point on strength and
nothing happens because we're not applying any abilities here.
And remember, our attributes are using a passive ability.
Listen for events because we're sending gameplay events when we upgrade our attributes.
Well, when we're loading in and it's not the first time, we don't apply that gameplay ability we need
to load in our gameplay abilities if it's not our first time loading in, so we're not loading those
in yet, we need to do that.
That's going to be next when we figure out how we can save our gameplay abilities, so that's coming
right up.
But other than that, we're able to load in our attributes.
We have two spell points there and that's great.
So a couple things right.
First, I'd like to make sure that we show our level here in the load screen.
And then we want to save and load abilities.
So we'll do those things next.
Great job and I'll see you soon.