Welcome back.
Now my enemies can get burned, which is cool, but I also would like to see something while that's
happening.
So while the enemies are sitting there roasting, I want to see a fire effect similar to my pillars
of flame here.
So I'm going to create a Niagara component for that.
Now notice I didn't say add a Niagara component.
I said create one.
Why?
Because the Niagara system component is a component like our other components.
It just has a Niagara system on it.
But if we derive a new C plus plus class from Niagara component, we can have a Niagara component that
can activate and deactivate itself depending on stuff that's going on with its owner.
That is a clever solution, if I do say so myself.
So I'm going to create a new Cplusplus class in the Ability system folder right there.
And it's going to be based on Niagara component.
So I'm going to choose Niagara component.
And in the Ability system folder I'm going to make a new folder called debuff because I want to stay
organized.
And this is going to be a debuff Niagara component a special subclass of Niagara component with additional
capabilities.
We're going to create this class.
We're going to close the editor and we're going to open up this class.
So I'm going to go ahead and close all tabs and open up my public folder.
Go to Ability System and wait for it.
There it is.
Debuff got a little nervous there.
It took a little longer than normal okay.
So we have a Niagara component Specifically for debuffs.
Now, what does that mean?
What are we going to use this for?
Well, for one, a debuff Niagara component should be identified by a gameplay tag.
I'm going to go ahead and make a public section and add an F gameplay tag called Debuff Tag.
Now debuff Tag gets a new property and we'll make it edit defaults only.
And in addition to a gameplay tag, I'm going to declare and define a constructor for one reason and
one reason only, and that is to set be auto activate to false.
So the Niagara system on this component would auto activate if this were true.
Now we won't have goblins sprouting up with flaming heads, although that would be cool.
So now that we have this Niagara component, we need to know when to activate and when to deactivate
it, and the Niagara component has activate, which you can use to activate its Niagara system.
And it also has deactivate to deactivate it.
And I'd like to activate and deactivate this when the owner of this Niagara component gets the debuff
tag.
And that's going to be your quest.
Responding to debuff tags, I'd like you to activate the Niagara component when the owner of the Niagara
component gets the debuff tag, and then deactivate the Niagara component when the owner loses that
tag.
That way, we'll be able to see visually if that owner has a debuff.
So pause the video and give this one a try.
Okay, so we need to do a few things here.
For one we're going to need Beginplay.
So I'm going to add a protected section and override began.
Play virtual void.
Begin.
Play.
Override.
Now I'm also going to create a debuff tag changed callback so that we can bind it to a delegate on the
ability system component for when a debuff tag has changed.
So I'm going to make a void function called debuff tag changed.
Now the signature is going to be a const f gameplay tag, not a const reference.
And it's called callback tag and an int32 called New Count.
And you probably went and checked out your other tag change callbacks because we have those two elsewhere
in our project.
Well, let's go ahead and generate definitions for these.
And we can call super Beginplay.
We can get our debuff tag changed function there.
And debuff tag change is going to be quite simple.
We're going to check if new count is greater than zero, then we can activate if it's not.
In other words, we have an else case we can deactivate.
So that's pretty simple.
So we're going to need debuff tag changed bound to a delegate.
And we're going to do that in Beginplay.
So how do we do that.
Well we need the ability system component of our owner.
So we can use you ability system blueprint library.
We'll get that auto include there.
We'll call get ability system component of well our owner.
So we're going to call get owner.
That'll give us whoever owns this Niagara component which is going to be whoever we add one of these
to.
Once we have that ability system component which we can store in a local pointer, let's do that new
ability system component ASC.
Once we have that, then we can use it.
But the thing is, the ASC might not be initialized yet at this point in time.
So what we'll do is check if ASC and if it is then we can take ASC.
And what we're looking for is register gameplay tag event.
And this requires the tag.
Well we have the debuff tag that member variable.
And we're going to use E gameplay tag event type new or removed.
Now this gets us the delegate we're going to use add Uobject passing in this and our function callback
debuff tag changed.
So at you debuff Niagara component debuff tag changed.
Now what about the situation where asc is a null pointer?
The owner may not have a valid ability system component yet.
Now that's okay if you didn't take this into account, but I'm going to.
So how do we know if our ask becomes valid?
Well, the easiest way is to create a delegate and broadcast it from the owner as soon as that ability
system component is valid.
So I'd like to make a delegate that we can broadcast when that happens.
But I don't want my debuff component dependent on the aura character or aura character base.
So we're going to make a function on our combat interface that can return that delegate.
And for that matter, we can declare that delegate on the combat interface itself.
So I'm going to open my combat interface and declare a new type of delegate.
It's going to be a declare multicast delegate one param.
And I'm going to call it F on ASC registered.
And the reason I'd like it to be one param is I'd like to broadcast that ability system component as
soon as it's valid.
So I'm going to broadcast a usability system component.
I'll go ahead and add a forward declaration for it, and we'll pass it through by pointer.
And now our combat interface can have a function to retrieve it.
So a function that returns an F on ASC registered and we can call it get on ASC registered.
We could call it delegate if we want to.
And I'm going to make this a virtual function.
In fact I'd like it to be pure virtual.
So I'll set it equal to zero there and make it pure virtual.
So it's forced upon anything that implements this.
They have to override it.
And I'm not going to expose it to blueprint just here in C plus plus.
And I'm going to open my aura character base and add one of these delegates an F on ASC registered called
on ASC registered.
And we'll implement that pure virtual function.
Get on ASC registered.
We'll add override to it and implement this function.
And all we're going to do in this function is return on ASC register.
Now that we have an easy way to get that delegate on the owner, we'll do it here.
So we'll have an else case.
But I'm going to make it an else if so that I can check if the owner implements the combat interface.
So that resulted in combat interface being added.
When I created this pointer I'm going to call it Combat interface.
And I'm going to cast it to combat interface I combat interface.
What am I casting?
I'm casting get owner.
So if the owner implements this interface then I can call combat interface, get on ASC registered delegate
and I can bind something to it.
Now we can bind a function.
We can bind a lambda.
I'd like to add a lambda, but instead of using add lambda, I'd like to use add weak lambda.
So what does that mean?
What does add weak lambda?
Well, a weak lambda is a little bit safer than a regular lambda because it has an additional parameter,
a u object that it takes in.
And internally, the weak lambda wraps this u object in a weak pointer, which does not affect its garbage
collection reference count.
But what it does do is it keeps track of that object and knows when that object is no longer valid.
It checks that weak pointer to see if that object is still valid.
If it's not, the lambda will not be executed.
So weak lambdas are a safe way to bind a lambda, specifying an object to be checked when that lambda
is to be called.
So this can help us to prevent trying to access a pointer that points to a memory address that's no
longer valid.
The object that it used to point to perhaps was garbage collected or destroyed or marked pending kill.
Weak lambdas will not be executed if that object passed in, as its first parameter is no longer valid.
So I'm going to use add weak lambda.
Now add weak lambda is different than add lambda in that it requires a user object first and then the
lambda.
So we're going to make the lambda here.
Now I'll go ahead and put the body on its own lines.
And the signature has to be for a function that can take a usability system component.
So I'm going to call it in ASC.
And in ASC I can do exactly what we're doing up here.
We can register our callback to the gameplay tag event.
Of course if we want to use this and debuff tag, we have to capture this.
So now that we have this, we can make sure that we're broadcasting, get on, ask registered delegate.
We want to make sure that this delegate is broadcast.
And that's really going to mean something different depending on which character, whether it's or a
character or enemy, if it's a or a character, we're going to do it in init ability actor info here.
After our ability system component is valid, we know that it's valid after we set it here so we can
broadcast on ASC, registered here.
So on ASC registered broadcast and we can pass in the ability system component.
But on Ora enemy that's going to be after it calls init ability actor info here.
So I'm going to broadcast on ASC registered after it has its actor info initialized.
So we'll broadcast it here.
Broadcasting ability system component.
Now, yes, we're calling it in both an ability actor infos and we could just call it in or a character
base init ability actor info.
Then we could call super.
But the problem with that is is I don't want to do that because I'd have to remember to call super after
we set our ability system component, not before.
And that's error prone to developer error because it's kind of fragile when everything breaks.
If you call super in the wrong place, and I just don't want to do that.
So we're just going to call it here.
So now we know on ASC registered will be broadcast.
Now this is all great.
Except there's another thing that we're not taking into account.
And that other thing is what about when the character dies?
When the character dies, we're going to want to call deactivate.
Just like when a debuff tag has been removed.
Right.
So I'd like another delegate for when the enemy dies that we can broadcast.
That's going to be really useful even in other circumstances.
But we need to be able to get that, too.
So I'm going to declare it on the combat interface and go through these same steps as I did for the
on ask registered delegate.
So back to the combat interface, but I'm going to make this one a dynamic multicast delegate, one
param, because I'd like to be able to bind to this in blueprint, we're going to see all kinds of useful
reasons to want a delegate bound for when something dies.
So I'm going to declare a dynamic multicast delegate, and this will be one param.
And I'm going to call this F on death.
And on death is going to have one param and it's going to be an A actor.
That'll be a pointer.
And with a dynamic multicast we put a comma followed by the name of the param I'm going to call it dead
Actor.
And I'm going to create just another function here, just like the last one virtual.
And it's going to be on death delegate.
We'll call it get on death delegate.
And it's going to be pure virtual and it needs a return type.
It's going to return F on death.
So same thing.
We'll go back to our character base.
We'll give it an F on death.
Delegate called on death.
And it's actually just called F on death.
So we'll call it on death super short for a delegate name.
And we'll override the function virtual F on Death.
Get on death.
Delegate.
Override.
Let's go ahead and implement it.
And return on death.
And we'll go ahead and go back to our beginplay on the Niagara component.
And now I have a reason to get this eye combat interface outside the ElseIf, because I want to bind
to this whether our ASC was initially valid or not.
So I'm going to go ahead and make this combat interface just a variable here.
And in the ElseIf we'll just check combat interface.
But regardless I'm going to also bind here.
If combat interface then I'm going to take Combat Interface and call get on death delegate.
And I want to add a function to that.
Now this is a dynamic multicast delegate.
So I'm going to have to make a new function for it.
I'm going to call this void on owner death.
And this has to receive an A actor.
We'll call it dead actor.
Generate the definition for it.
It's just going to deactivate.
That's all it's going to do.
It's still useful for this to broadcast the dead actor.
So I'm going to keep that there.
And it doesn't need to be inline.
So I'm going to move it to the CPP.
And if we're going to bind it it has to be a U function.
And we can bind it here with add dynamic as it's a dynamic multicast.
So we'll use this and at U debuff Niagara component on owner death.
Okay.
So that is basically everything we need here.
Now another thing I want to do is broadcast our on death Delegate.
Now that's something we could do in Oracle database because we know when we're going to die.
In fact, handle death.
Multicast is the perfect place to broadcast this.
We can take on death dot broadcast and broadcast this because we have to broadcast that dead actor.
And with that, everything is tied together.
And we can add one of these debuff Niagara components to our Aurora character Base class and construct
it.
So here in Aurora Character Base, I'm going to add this to the protected section all the way down at
the bottom we're going to make a T object pointer of type U debuff Niagara component.
I'm going to add the forward declaration and I'm going to call this fire debuff component.
And in fact our fire debuff is called burn, so I'm going to call it Burn Debuff component sounds cooler.
I'm going to give it a Uproperty with visible anywhere so we can see it.
And we'll be able to set its properties.
And I'm going to construct this in aura character based CPP.
So I'm going to do this at the top just under primary actor tick.
I'm going to use Createdefaultsubobject and specify you debuff Niagara component.
So see our aura character base is dependent on Debuff Niagara component, which is a good reason to
not let debuff Niagara component be dependent on Aura character base.
We don't want that circular dependency.
We're going to give it the name of Burn Debuff component and we're going to attach it.
So Burn Debuff component.
We're going to call set up attachment.
We're going to attach it to get root component.
So we'll attach it to the root.
We'll still be able to adjust it and we need to set its debuff tag right.
So burn debuff component debuff tag will set it.
And we're going to need F or a gameplay tags.
We'll call Get and get debuff and burn.
And we made Debuff Niagara component edit defaults only didn't we.
Now we can set it to visible anywhere because we don't need to set it from blueprint.
We're just going to set it here.
And with that, all we need to do is set a Niagara system on this component and test it out.
So let's try it okay.
So we're back in the editor.
And really what I need to do is open up my character classes.
I need to go into character and BP enemy base.
And here's my burn debuff component.
We can set the Niagara system asset, and I'm going to search for fire.
And as fire will do because I have that nice fire which looks like this.
We've seen it.
We're going to use this now.
We're going to need to adjust it relative to the capsule, but we're going to need to adjust it per
enemy.
Right.
So what I want to do is go to my goblin, just edit that and get my burn debuff.
And what I can do is take auto activate and check it.
And now I'll see what it looks like.
And then I can move it around.
And that way I can make their heads be on fire.
I could put it on their chest like this.
I kind of like their heads being on fire.
I think it's funny.
So I'm gonna put it there, uncheck auto activate, compile, save and then I can go into my other enemies
like Goblin slingshot and do the same thing.
So I'm going to take the burn debuff, search for auto activate, check that, and then make sure their
head is on fire.
I'll do the same thing for the shaman opening that taking the burn debuff.
Search for auto activate, check it and make sure this head is on fire.
It's got a wooden mask, so that's pretty flammable.
I think that looks good.
And we're going to do it for Ghoul Demon.
And that's it.
Let's go to ghoul.
And we'll select it.
Auto activate on.
We can just go ahead and make its head burn or really doesn't matter.
We can make it back here.
I think it kind of looks cool if it's back is on fire, maybe even down on the back a little more.
Something like that.
Okay.
Checking that, I also want to check it so the auto activate isn't active on any of them.
And finally I'll go back to my daemon.
We have two daemons, so we need to add it to both of them.
In hindsight, it would have been nice if I had just lowered it on the base and then raised it for the
bigger enemies, because most of the smaller ones are about the same height, so the daemons can be
on fire.
Can I uncheck that and set it for the last enemy?
Like so.
Okay, so now that those have their debuffs, the last thing is to set it for aura, because maybe aura
might get burned too.
So I'm going to go to aura and select Aura Burn debuff check auto activate for that.
And of course I have to set the Niagara system to NS fire.
And we can see what it looks like for aura.
And I think burning Aura about like that is going to look good.
Okay.
With all that we can go ahead and test things out.
So I have 100% chance to burn.
Even aura has 100% chance to burn.
Look at that.
I'm going to fix that real quick.
Auto activate off.
There we go.
Okay, so all we need to do is cause some damage.
And there we have an enemy getting burned.
And it died and the flames went away.
Of course, it was there for five seconds, which is our debuff duration.
But if we set our debuff duration to 10s, then we can really test it out.
Because then we'll see that the enemy will die.
It hasn't been 10s yet and it was deactivated.
We can also test, say one second and make sure that the debuff only lasts one second.
There it goes.
Perfect.
And that's looking pretty good.
So with that we now have debuffs.
We have burning.
Excellent.
I'll go ahead and leave that debuff duration at five.
And with that we can go ahead and close all these other tabs.
We now have a working burn debuff.
Off.
And even though my game play ability has 100% chance to burn, we can lower that to whatever we want.
And now our debuff system is in place, and it's actually quite robust because all we need to do to
add a debuff to a gameplay ability is just set parameters and everything else is in place.
If we go to our abilities lightning electrocute, we'll see in the class defaults, well, we don't
see anything, but if we change the class settings and inherit from aura damage gameplay ability.
Then we'll see.
In class defaults, we can set debuff chance and other parameters, and once we set the damage type,
our debuff will just work.
So we have a really nice system in place.
It took some work to set up, but it will take very little work to use it as we create our game.
So great job on this one and I'll see you in the next video.