Welcome back.
Now that we're applying damage effect well we're not applying it, but our apply damage effect function
is now setting our radial damage parameters.
We can harvest these at any point along the damage pipeline where we can access the context handle.
And I'd like to access it from within the exec calc.
So I'm going to close all tabs and go into my exec calc and Ability system exec calc damage right here.
And here's where all the magic is happening with damage, where looping through all damage types to
resistances that map that exists in or a gameplay tags.
And what are we doing?
Well, we're getting our resistance that corresponds to that damage type and where decreasing the damage
based on that resistance.
So that's all fine.
That's all good.
But after we've done that, what we need to do with that damage is determine if we have radial damage.
Because if this is radial damage, then we need to scale it down based on the distance of the victim
from the origin of damage.
So at this point, after we've checked and reduced our damage based on resistances, we can then see
if we need to lower it even more based on the distance, if it's radial, and we can do this per damage
type.
So if this is radial damage, we're going to be scaling the damage down.
Now if we're doing this per damage type then we have a damage type value.
And we can scale that damage type value down before adding it to the damage total.
So before incrementing damage for this given damage type but after we've taken resistances into account,
we can then check to see if we need to apply radial damage.
That's the order I'm going to do it in.
You could do it before taking resistances into account.
It's up to you.
So how are we going to do it?
Well, we need to check our context.
We need our gameplay effect context, don't we?
Now if we search here for context, we'll see that we have f gameplay effect, context handle spec dot
get context.
So here's our effect context handle.
It's right there.
And we're getting it.
It looks like below where we're calculating damage.
But that's not to say that we can't just get it higher up before we need it so that it's in scope.
So I'm going to control X to cut it and move it up towards the beginning of the function way up towards
the top.
We'll just do it as soon as we have the spec right here, we can get our gameplay effect context handle
then.
And now when we're checking damage, we can check that context handle to see if we have radial damage.
So I'm going to say if you aura ability system library is radial damage passing in that effect context
handle.
If it is radial damage, then what are we going to do?
You see, we could apply radial damage with falloff using the U gameplay statics function, but that's
just going to result in that actor that we damage having its take damage function called.
But how are we going to know the result of that once that happens?
Well, that's what delegates are good for.
If we get a delegate we can bind to, we can even bind a lambda to it right here.
Then we can call apply radial damage with falloff.
And in the character's take damage function we could broadcast the final damage amount.
So it's kind of a roundabout way, but it's also a clever way.
So I'm going to map this out because that might have sounded a little confusing.
First I'm going to say one.
Let's put some pseudo code here.
One we're going to override take damage, and we'll do this in the base character class in our character
base.
Two we're going to create delegates and we can call it on damage delegate and broadcast damage received
and take damage.
Three we're going to bind to on damage delegate on the.
We'll call it the victim.
Here.
For?
What?
Are we going to bind it?
Let's just say we're going to bind a lambda.
And then in four we'll say call apply radial damage with falloff.
That's you gameplay statics you gameplay statics apply radial damage with falloff to cause damage and
parentheses.
This will result in take damage being called on the victim.
Which will then broadcast on damage delegate.
And five.
In Lambda here in our response, set damage type value to the damage received from the broadcast.
So now these steps are planned out and it's a little bit more clear what to do.
And we have a list.
So this is how we're going to implement this.
Now how are we going to bind to that delegate from right here without say casting to or a character
base, which is a practice that I tend to shy away from because it makes the exact calc very specific.
I like to use interface functions instead, so we're going to probably end up using an interface, but
it's nice when we have a set of steps like this, because then we can just follow the steps and it makes
the task a lot easier.
So let's take this one step at a time.
Let's override take damage in or a character base.
So we'll go ahead and open our public folder, go to Character Get or a character base open.
And we'll just do it right here in the public section.
Virtual void take damage.
Now it's not auto completing for me because it's actually not a void function.
Take damage returns a float.
That's also one of the reasons why it's so useful, because whatever take damage receives as a result
of gameplay statics calling it, or anything else in the damage system it receives, the amount of damage
taken, and then whatever we decide to return here is returned, so we can return that damage as well.
So take damage takes in a float.
In fact, we should be able to get auto complete to work for us.
Now I'm just going to type it again.
Virtual float.
Take damage.
There we go.
Now writer auto completes it.
So take damage.
Takes in damage amount as a float and if damage event const called damage event and a controller called
instigator event instigator rather and an a actor called damage causer.
So with that we can go ahead and generate a definition.
And now we've overridden take damage.
Perfect.
That was step one.
Let's go back and look at our step.
We can add a check mark.
I'll just use an asterisk to say we're done with this step.
Next create a delegate on damage delegate broadcast damage received and take damage.
So we need a delegate so we can go back to our character base and we can declare a delegate.
We can go all the way up to the top above our includes and just declare one here, and I'm just going
to make it multicast.
I only plan on binding to it and C plus plus.
So declare multicast delegate.
And it needs to broadcast one param, which is going to be the damage amount.
So I'm going to call this F on damage signature.
It's going to broadcast a float and I'm going to just put here damage amount.
So it's clear that that's what that float is for.
Then we can add a public variable here for this.
I'll put it down here with our delegates and I'll call this on damage delegate.
And then let's look at our list.
It says create the damage delegate on damage delegate broadcast damage received and take damage.
So here in our character base, what I'm going to do is after super has calculated take damage, I'm
going to store that in a float called Damage Taken.
And I'm going to broadcast that on damage delegate dot broadcast damage taken.
And then this function can return damage taken.
So pretty simple.
And this can even be, you guessed it, a cost.
So take damage as simple, it just takes damage and broadcast the delegate.
So now in exact calc damage that step is done.
We can go on to the next one.
Bind a lambda to on damage, delegate on the victim here.
And this is where interfaces come in, because we don't want to have to cast to or a character based
just to get that delegate.
I'd like a function in the combat interface that returns it.
So let's go to combat interface and Interaction and we'll create one.
And just like our other interface functions that return a delegate, we should return a reference.
So we return the actual delegate without making a copy of it.
So I'm going to just make a pure virtual function called f on.
Damage signature.
And you know what?
I'm just now realizing that I declared this in or a character base if I use it here as a reference.
I'm going to have to forward declare it, which I can.
Actually, it's a reference.
I can forward declare it here and I'm going to call this function get on damage signature.
And this will be pure virtual.
That should work and I can forward declare it up at the top, since that's the convention I've been
using for this project, and it's good to stick to conventions.
So we'll do that.
And now we don't need to forward declare there.
And now we can override F on damage signature and or a character base.
Let's do that.
So right here virtual F on damage signature ref get on damage signature override.
Okay.
So we're not going to be able to override this.
And that's because we're overriding a function that has an incomplete type over in the combat interface.
To make this work, what we should do is we should instead declare the delegate.
In the combat interface.
So I'm going to move that declaration into the combat interface and remove that forward declaration
here.
And it's not going to need that forward declaration here.
Instead, we're going to use the forward declaration in or a character base.
So we'll say struct.
F on damage signature.
So we're not actually going to even need to forward declare it here in our character base.
And with that, we are now overriding a valid function and we can go ahead and provide the definition.
By the way, if you were wondering if you could just define these on the same line like this return
on damage delegate.
Sure you could.
Of course.
Now it looks weird having this one and the others are not doing it.
So rather than do that, I'm just going to be consistent and we'll return it here.
Return.
On damage.
Delegate.
Okay.
So now our our character base is implementing that.
And what we can do in exec calc damage is we can do.
Step three bind a lambda to on damage delegate on the victim here.
That's going to be right here.
So how are we going to do that.
Well we should have access to the victim.
So if we scroll up what is our victim.
Well that's going to be the target.
And we have the target avatar right here.
That's pretty convenient.
This is the thing getting damaged, right?
The target here in Exec Calc.
So what we can do is we can cast the target avatar to our interface.
We can say I combat interface.
Combat interface.
As long as we haven't used combat interface already.
Let me just search for it.
Okay, so we're checking if Source Avatar implements it up here.
But this is actually the only place where we're casting to it.
So this is fine.
So we're going to cast to it.
Cast to AI combat interface.
Casting.
Target.
Avatar.
And we'll go ahead.
Wrap that in an if statement.
And then we can take our combat interface and we can call the function get on damage signature.
Now this returns that delegate.
So we can just bind to it.
And I'd like to bind a lambda just like my steps here are telling me to do.
So.
For that I can use add lambda.
And I can make my lambda here.
Square parentheses squigglies.
And what am I going to do in here?
Well this delegate broadcast afloat the damage amount.
So I'm going to go ahead and use a float damage amount as my input parameter.
And all I need to do in this is set my damage type value.
Equal to damage amount.
Now I can't set damage type value unless it's captured and I can't capture it by value.
I have to capture it by reference.
So by the way, you can just put a ampersand in here.
And now you're capturing everything in here by reference.
So setting damage type value in here will set damage type value out here.
Pretty nifty.
Now we're not just overwriting damage type value and getting rid of the work we've done here for resistances.
Why are we not doing that?
Well because this all happens in response to the character's take damage function.
And take damage is going to receive the amount of our damage type value, but after taking the radial
damage distance into account.
So it's going to start off with damage type value and then nerf it based on distance.
How is it going to do that?
Well, we have to call apply radial damage with falloff.
That's our next step.
So we've bound the lambda.
We can check off that part of the list.
And now we're going to call you gameplay statics.
Apply radial damage with falloff to cause damage.
This will result in take damage being called on the victim, which will then broadcast the on damage
delegate which we have lambda bound to here.
And then in Lambda set damage type value to the damage received from broadcast.
So we actually jumped the gun here and we did that step a little early.
Now we just need to call number four.
So what we do here is after we've bound the lambda here after that if statement there we're going to
call apply radial damage with falloff.
That's a you gameplay statics function.
And I'm going to go ahead and make that include happen.
So I can get auto complete.
So apply radial damage with falloff.
And this takes a ton of parameters.
We need a world context object.
I'm just going to pass in the target avatar.
We need the base damage amount.
And that's why I said that damage type value isn't just thrown out the window.
We're passing in damage type value to this function now because we have a ton of input parameters.
Let's start getting these on their own lines here.
And after damage type value we have a minimum damage.
I'm just going to use zero.
I mentioned earlier that I would.
So if outside of the damage radius will actually not receive any damage outside of that damage outer
radius, that is.
Now we need the origin.
And this is where our handy ability system library functions come in.
We're going to call you or Ability system library get damage origin or get radial damage origin.
And we have to pass in our effect context handle in addition to that.
Next we have damage inner radius and damage outer radius.
So back to our ability system blueprint library functions get radial damage inner radius passing in
the effect context handle.
Next is the outer radius.
So we'll get that.
So radial damage.
Outer radius passing in the effect context handle.
Now damage falloff.
We've seen that we'll get a nice linear falloff if that's one.
And a faster sharper falloff exponentially if it's larger than one.
Now for damage type class we can just use you damage type static class just to satisfy that input.
Now we need an array of ignore actors.
And I'm not going to ignore any actors here, so I'll pass in an empty array.
Of a actor.
Pointers.
Now for the damage causer.
We have a source actor.
And that is not exactly what I wanted.
Source actor is what I wanted.
Oh, and it's auto completing because there is no source actor.
It's called Source Avatar.
So just like we have a target avatar, we have a source avatar.
I'm going to pass that in.
And finally an instigated by controller.
Well, if we wanted to pass in a controller and use it, I don't have any use for it.
But we could go on and say CoStar, source Avatar, and target avatar to characters.
I don't think I need to do that.
I'm going to pass in a null pointer.
I don't plan on using that.
So with that we're calling Apply radial damage with falloff.
Now here's the trick.
It's like a sleight of hand trick.
Right.
Because what we're doing is we're being sneaky here.
We're taking our target actor that's getting damaged.
We're getting its combat interface and getting the on damage signature and binding a quick little lambda
to it and saying, hey, whenever you broadcast this delegate, my lambda is going to respond and it's
going to set my damage type value to the damage amount, and then it turns around and causes damage
to that actor.
Right.
And this happens right away.
Gameplay.
Static says, hey, calculate radial damage with falloff, then apply damage.
And then of course, this actor that we just bound a lambda to instantly gets take damage called and
it broadcasts this.
So it's kind of a funny little loop that happens right here.
But it causes radial damage.
Pretty cool right.
So that's pretty amazing.
So with that we have just completed that step in our radial damage capability.
We've added radial damage to our pipeline.
All we have to do is set those radial damage params and then we'll get radial damage.
So pretty exciting.
And we're going to go ahead and just compile this.
And then we'll get all this working in the next video where we tie it all together and set our damage
params for our arcane shards.
Excellent job and I'll see you in the next video.