Welcome back.
So you've probably noticed some funny things about our ability.
For one, those beams never actually go away.
If we kill those enemies, our beams are still going, and that's not good.
So we need a way to know when the enemies die.
And we should stop applying those beams.
And I'd like to end the ability as well, as soon as either the primary target dies or perhaps the last
of the targets dies.
So we're going to work on that in this video.
First of all, we need to know when one of these enemies dies.
And I'd like to have a couple of events that I can implement here and control what happens when either
the primary target dies or any of the additional targets has died.
So I'm going to create a delegate that can be broadcast and we can respond to it here.
I'm going to create that delegate in C plus plus.
And I'm going to create it in the interaction folder in combat interface, because I'd like to create
a function that can retrieve this delegate from any actor that implements the combat interface.
So I'm going to declare it.
It's going to be a dynamic multicast delegate.
And it's going to have one param.
I'd like it to broadcast a pointer to the dead actor that just died.
And I'm going to call this F on death signature.
And it'll take an actor and we'll call it dead actor.
Now, I'd like a function to get the on death signature, so I'm going to make that function here in
the combat interface.
I don't need it to be exposed to blueprint because I plan on binding to it here in C plus plus.
So I'll make it a pure virtual function.
And it's going to return an F on death signature.
But I need to return a reference to it.
This is important because if we're getting an F on death signature, it's important that we don't return
a copy of that delegate.
We have to return a reference to the actual delegate on that character that implements this interface.
So very, very important to have that little ampersand there.
And we're going to call this get on death delegate.
And we're going to set it equal to zero to make it a pure virtual function.
And we'll go ahead and implement this function on the base class for our character.
That's our character base.
So just under our on ASC registered delegate we're going to make an F on death delegate called on death
delegate.
And we'll implement this function I'll put it right under die.
So virtual F on death signature reference.
Get on death.
Delegate override.
I'll go ahead and generate the definition and just have it return on death.
Delegate.
Okay, so I'm getting an error about F on death delegate.
And of course it's not an F on death delegate.
It's an F on death signature.
So that fixes that problem.
Okay.
So now we have an interface function we can call to get this delegate.
And where are we going to bind to it.
Well I'd like a couple functions on my aura beam spell.
So I'm going to go into aura Beam spell.
And I like these functions to be blueprint implementable events.
So I can decide what happens when they get executed in my ability blueprint.
So the first one will be void.
And I'm going to call this function primary target died.
And it has to take an actor pointer called dead actor as this is going to be a callback for my on death
delegate, it's going to be a new function and I'll make it a blueprint implementable event.
Now I'm going to copy this.
And I'll have another one for when an additional target dies.
So I'm going to have this one called additional target died.
And it'll also take a dead actor.
And we're not going to generate definitions because they're blueprint implementable events.
But we do need to bind these delegates.
And we can do that in or ABM spelt CPP, because we have access to the first target as soon as we trace
for it.
So what we'll do here at the end of the function after we've set our mouse hit actor is we'll see if
that mouse hit actor implements the interface first and foremost.
So we're going to say if I combat interface we're going to get a combat interface.
We're going to cast that mouse hit actor to an eye combat interface.
So mouse hit actor there.
If it does implement the interface, then we're going to take the combat interface and we're going to
call get on death, delegate and call Add Dynamic to it.
So the user interface is this.
The delegate is at you or a beam spell.
And this is going to be the primary target died blueprint implementable event.
Now I don't want to bind to this delegate if I already have.
And there's a way to check that.
I'm going to say if not, and I need to get this delegate.
So I'm going to copy the call to get on death delegate from the combat interface and hit Dot and I can
check is already bound, and I can check to see if this delegate is already bound for this particular
gameplay ability and this particular callback.
So I'm going to check that and only bind this callback if it's not already bound.
Now.
That's good.
That takes care of the primary target.
But we're going to do the same thing for additional targets.
And we have them here in store additional targets.
We can loop over the out additional targets array.
So I'd like to do that too.
I'm going to say for a actor.
We'll call this target in out additional targets.
And for each one we're going to check to see if it implements the interface.
So if I combat interface combat interface we'll cast to I combat interface.
And we're going to cast the target this time.
Now, if it does implement the interface, we're going to do the same thing we did here, namely check
to see if that delegate is already bound.
Only we want to check against our additional target died function.
So we're checking if that is already bound and then we're binding additional target died instead of
primary.
All right.
So now we know we'll have blueprint implementable events fired in response to this delegate.
We just need to make sure it's broadcast.
And we can broadcast the delegate in our aura character base and multicast handle.
Death is the perfect place for broadcasting it.
It'll broadcast on server and clients.
So we're going to take on Death Delegate.
We're going to broadcast it and broadcast this as this is the actor that's dying.
Okay.
So with that, we can go ahead and launch the editor and implement those blueprint implementable events.
All right.
So I'm going to open my ability.
And in the event graph I'm going to implement both of these new events.
The first one is primary target died and the other one is additional target died.
Now first we'll do the primary target died.
If our primary target dies.
I know that's my mouse hit actor.
So I can take the dead actor and remove gameplay cue on actor.
And that's going to be that first target cue params that we can pass in.
And the gameplay tag is going to be shock loop.
Now, in addition to removing the gameplay cue, I'd also like to commit the ability cooldown.
So I'm going to call commit ability cooldown here.
And we also need to show the mouse cursor.
Set the owner character to no longer be in shock loop, set its movement mode, all of that stuff.
So that's basically all we're doing in prepare to end ability.
If we go into that, we'll see that we show the mouse cursor, we set in shock loop, we set movement
mode.
We even see if our first target implements the interface.
And if it does, then we take the mouse hit actor, remove the first target, and we remove the shock
loop cue from all the additional targets.
And if the first target doesn't implement the interface, which we know it does because it just died,
right, then we remove that first target cue params from the self.
So basically this is going to end the entire ability.
So we can do that and we can then call end ability.
And in fact, if we wanted to clear and invalidate that timer so it doesn't keep playing our damage
and cost, then really we could just replace these with clear timer and end ability if we wanted to
do that.
Now when an additional target dies, this will be a little different.
All I want to do in this case is just remove the shock loop queue and we have Add.
Didn't want that one.
I wanted remove shock loop queue from additional target.
We can call this.
And we can look at that.
We see that we just remove the queue by looking it up in our map.
And after we do that, we can actually remove that target from our additional targets array.
So we can get that.
And we can call remove from it and we can remove an item.
We're going to remove the dead actor.
So.
Rerouting that up to here.
There we go.
And if primary target has died, we are ending the ability.
So I know I created this boolean out here.
Target dead, but it doesn't really matter.
We're ending the ability as soon as that target dies anyway.
So really, when we commit the ability cost, all we're going to do is apply damage.
So I'm going to simplify this a bit.
And if we can't pay that cost, not only do we clear the timer and end the ability, we can actually
handle the cooldown in clear timer and end ability, I think.
So I'm going to take commit ability cooldown.
I'm going to cut that and we'll stick it in clear timer and end ability.
And before we end the ability, we'll commit the ability cooldown.
So let's take a look at this.
Okay.
So a couple of things.
For one, we're trying to call git ability system component and getting a null pointer basically.
And because this is instanced per actor, as soon as we fill in that array, that array still has actors
in it.
So if I click over here.
We're still causing damage to those actors.
And so what we need to do is clear and clean up some things when we end the ability.
So before calling end ability here, we need to empty our additional targets.
And we also need to empty additional actors to queue params.
So we're going to call clear on the array.
And we're going to call clear on the map as well.
Well, we should also do.
Is we should set our variables.
Such as mouse hit Actor.
I'd actually like to set that set mouse hit actor, and I'm going to set it to nothing.
Like so.
And we also had a boolean first target implements interface.
We're going to set that to false as well.
Okay.
All right.
Now we're accessing none.
And this is where we're trying to call apply damage single target here.
So as soon as some of these enemies are dying and we're continuing to try to apply damage, that's why
we're getting a null pointer basically.
So in apply damage single target, we should check if that ability system component is valid.
We're going to say is valid here.
And if it's not valid, we're not going to continue.
Okay.
Looking better.
Okay.
So we're still getting that error.
And that's because this node is being evaluated multiple times.
So the best way to avoid that issue is promoting this to a variable.
And we'll make it a local variable and we'll call this ASC.
And we'll just check this with as valid here.
And then we don't have to call this multiple times.
So we'll just get the ASC and hook it in here.
Right there.
And we'll also hook it in here.
Okay, so we're still getting it, and it looks like it's on damaged target.
The target that's being passed in.
Now, we don't have to continue if damage target is invalid either.
So we can go ahead and check is valid for that.
Right there.
Okay.
So in our apply damage single target, now that we have our damage target we're checking if that's valid.
We're checking if the ability system component is valid.
We're also taking our damage target here getting its ability system component there.
This one.
Here is the ability system component from actor info.
This one is the AC on the damage target.
We can check if this one is valid too because they'll be pending kill as they drop like flies, so we'll
only apply the gameplay effect on a target that's valid here as well.
Okay.
Now, the last thing is we added some things to our clear timer and end ability, but we're not calling
this in all cases, we really should.
So we can clear all this stuff.
So if we go to our event graph and just look through at each time we're ending our ability, let's just
make sure that we're ending it properly.
So after spawning the electric beam, in fact, let's go into spawn electric beam.
Okay.
We're not ending the ability from that at all.
We can close that.
And after that, we set our timer.
We commit the ability cost each time in the timer.
And if we can't afford to pay the cost, we clear timer and ability.
And that resets all our variables.
So that's great.
And we apply damage otherwise.
Now.
The other times we end, the ability would be on input release.
Now here we prepare to end ability and end the ability.
But clear timer and end ability also zeroes out those variables that we need to zero out.
So we need that here and as well as over here when primary target died.
Yes.
We're clearing the timer and ending the ability there.
And when an additional target died then we don't end the ability.
Okay, let's check this out.
Okay, it seems like we do have a second of cooldown time and we're not getting an error anymore when
we stop, but we're not getting the cooldown timer in our HUD.
And that all comes down to going into ability system data, ability info, and setting the cooldown
tag for our electrocute ability if we set that to cooldown.
Lightning electrocute.
Now, thanks to our data driven approach, our widgets will respond to the cooldown tag, so let's check
that out.
I'm going to release.
There's my cooldown.
So the cooldown doesn't start until we've finished our ability.
And that's the behavior I want.
And of course, when our primary target dies, our ability ends and we kick off the cooldown.
Cool.
So that's looking good.
Now, there's one last issue I want to polish on this ability, and that is if I just right click and
release.
We don't really get any lightning.
We definitely do get our cooldown now.
But we don't even finish our montage.
So I want there to be a minimum amount of time required before we can end the ability in weight input
release.
So to enforce that I'm going to have a minimum spell time.
So I'm going to add a variable.
This will be a float I'm going to call it min spell time.
Change its type to float compile.
And I want at least a minimum spell time of 0.5 seconds.
So how do I enforce that?
Well, notice wait.
Input release has a time held.
So we can check time held.
I'm going to go ahead and just promote it to a variable here.
But if I'm going to promote this to a variable, then I'm going to make sure to clear it out and clear
timer and and ability.
So I'm going to set time held here to zero.
Even though whenever I release it'll be set anyway.
But I'm going to set it and I'm going to set it on release here.
That's when it's valid anyway.
Now, after setting it here, I'm going to check if time held.
Is less than the minimum spell time.
So I'm going to get time held.
Check less than.
Minimum spell time and we'll have a branch.
And if time held is less than the minimum spell time, then I want to get the difference.
I want to get minimum spell time minus.
Time held.
Because this tells me how much time is left that I have to keep my spell active, and I'm just going
to delay for that much.
So that will be for my true case.
And then I can clear timer and end ability after that delay.
However, if time held is not less than minimum spell time, we can clear timer and end the ability
right away.
Like so.
And I'm just going to eliminate these crossing wires here.
It sure is a lot more work when you don't have a nice plugin to help you with those nodes.
Just have to say.
Okay.
And with that, our ability is looking pretty much done.
I'm going to comment this and say primary target died.
We are done.
I can even give this.
A bit of a reddish or orange.
And then this one.
We'll say.
Additional target died.
Remove from additional targets and remove.
Shock loop queue and I'll give that that orange color as well.
That looks a lot better.
And we can't put a delay in a function.
But we can comment this and say enforce minimum spell time.
In fact, let's test that out.
I'm just going to right click and release.
And there we had our minimum spell time.
So at least we'll definitely see our.
Electric beam.
And if we hold it, then we'll keep spending mana.
And if we release, we get our cooldown.
Excellent.
Okay, so that is our electrocute spell.
The only thing left is perhaps to actually set the number of beams based on our ability level, which
we have code in place for that.
And of course to set our descriptions.
So I'm going to close the editor and go back into Aura Beam spell and just find that line we commented
out here and comment out the additional targets equals five.
And now set it equal to our ability level.
And it looks like I called that other one num addition targets and this one num additional targets.
So I'm going to make sure I'm passing in the correct one.
I'll go ahead and compile that and I'll see you in the next video.