Welcome back.
Now, our goal here is to be able to apply an infinite effect and remove that effect on end overlap
if we need to.
If our removal policy is set to remove on end overlap for our infinite effect.
So the question is how do we do that?
Well, let's go back to where we're applying effects.
We have apply effect to target and apply a gameplay effect.
Spec to self has a return type.
It's not void, it's active gameplay effect handle.
So once you've applied a gameplay effect, that gameplay effect becomes active and these apply functions
return a handle to that effect.
So we can always use that handle for something later, such as removing it if it's an infinite gameplay
effect.
So we need to store this handle now.
We need to be careful about the way we do this.
You see, if we're just thinking about one player character in the world running around picking up these
effect actors, well then we could store the active gameplay effect that we've applied and then on and
overlap.
Just take that other actor and remove that active effect.
But realistically, games are not that simple.
We might have multiple actors overlapping.
Some of them may have ability system components, some may not.
And we can't just assume that target actor and overlap is the correct actor for any given effect handle.
And if multiple actors are overlapping and we're setting a single effect handle variable, well then
we're overwriting it and losing the effect handle that that variable stored before.
In other words, this requires a bit more care.
Now one approach is that as we apply a gameplay effect, if that gameplay effect is an infinite duration
gameplay effect, we can store the handle, but we can also store the actor that we're applying this
effect to.
Essentially linking the two together.
A map should come to mind.
Map data structures are great for linking two values together.
Now we can make two functions one for instant and duration effect application and one for infinite.
But in the name of education, I'd like to show you a way to figure out whether or not this gameplay
effect is infinite duration or not.
In other words, check at runtime the duration policy for a given effect.
I'd like to show you how to do that Now in order to check the duration policy, we have to take a look
at our effect spec handle.
So I'm going to take my effect spec handle.
And from this handle we know that the handle has a data member so we can access that.
We know that much and data is a shared pointer.
To get that raw pointer we can call get on that and that returns us a pointer to gameplay effect spec.
Now this is the effect spec.
What we want from the effect spec is the gameplay effect itself.
Now this pointer can be dereferenced and the gameplay effect itself is called def.
So inside the spec handle we have data that's the gameplay effect spec.
Inside the spec we have def, which is the gameplay effect.
Notice it's a t object pointer of type u gameplay effect.
Now this being a t object pointer means we have to call get yet again and that gives us the raw pointer
which we can access to get all of these things on the gameplay effect itself.
And one of those things is duration policy.
Now keep in mind we're accessing the class default object properties here and we don't want to use this
method to say change the duration policy at runtime, for example.
No, that's not what we want to do.
This is just to look and see what the policy is.
And we're assuming that this policy isn't changing at runtime, but we can check it and we can see if
it's equal to an E gameplay.
Effect, duration type as that's what the duration policy is.
It's an EA gameplay effect, duration type, and we can check to see if it's infinite or not.
Now, if it is infinite, well then we know that this expression will return true.
So we can create a const bool called B is infinite and set it equal to the result of this.
Now, if we wanted to be able to remove duration based effects on end overlap, we could then give our
duration based effect its own removal policy, just like we have for Infinite.
And that just depends on what you like your effect actor to be able to do this effect actor that I'm
making will only be able to remove infinite effects and they'll only be removed on end overlap.
Now if it is an infinite effect, I want to store for this infinite effect somehow.
And how do I store it?
Well, apply gameplay effect spec to self returns an active gameplay effect handle.
That's what I want to store and link up with the target actor.
So the return type from apply gameplay effect spec to self.
An active gameplay effect handle.
I'm going to store in a local variable called active effect handle and if B is infinite is true, then
I'll store it.
So I'm going to say if B is infinite, then store the effect handle and I'm going to store it in a map
and the map can map active gameplay effect handles and the value can be the target actor or even more
handy can be an ability system component.
That way, we don't have to dig into the actor to get the AC, we have the AC already, so I'd like
to make a map mapping active gameplay effect handles to ability system component pointers.
So let's make that map.
So I'm going to say t map and the types will be f active gameplay effect handle.
That's the key and the value will be you ability.
System component will add the forward declaration and that will be a pointer for that type.
And we'll call this active effect handles.
And we know it's mapping handles to ability system components.
Now we're not forward declaring this because it's not a pointer or a reference, so I'm just going to
hit control shift B and compile.
Just to see that undeclared identifier.
And that tells me I have to include the header.
I'm going to go ahead and include at the top here.
Gameplay effect types dot Age.
And that's going to take care of that compilation error there.
So I have a map and back in the CPP file, if our gameplay effect is infinite, I want to add to my
map Active effect handles the active gameplay effect handle, active effect handle and the target ability
system component.
So I'll take active effect handles and I can add this key value pair.
It's going to be active effect handle and target ASC and I get the red squiggles so I have to come back
here and check for typos.
It looks like I have you ora ability system component.
That's not what I want.
I just want you ability system component here.
Which means I forward declared the ora version.
I don't want that.
So I'm going to make sure I'm only forward declaring the base ability system component and that's going
to get rid of my red squiggles there.
So an easy mistake to make.
And writer says the active effect handle can be const, we can make it const if we like be is infinite
can be moved inside this if check if we want as well.
I'm going to leave it out and keep it like this as a matter of style.
So active effect handles now has a key value pair.
When we apply an effect to a target.
Now we should only be doing this if we plan on removing the effect right.
If we don't plan on removing the infinite effect, then there's no need to store a handle to the effect.
So we should be checking our removal policy.
If it's do not remove, we should not even bother storing it.
But if it's remove on an overlap, we should.
So if our effect is infinite, we'll also have an end here and we'll check our Infinite Effect removal
policy and see if it's equal to effect removal policy.
Remove on end overlap.
If that's the case, well then we'll store it and then on end overlap we'll remove it, but we'll check
that policy as well.
So an on overlap, we have to apply the infinite effect and on end overlap, that's where we handle
removing it.
So first we'll check to see if we should apply it.
We'll say if infinite gameplay effect application policy double equals E effect application policy apply
on overlap.
And if that's the case, we'll apply effect to target.
So calling apply effect to target passing in target actor and we'll pass in our infinite.
Gameplay effect class and in on end overlap, we can check that policy again, but we can change this.
We can see if it's apply on end overlap and we can apply the gameplay effect.
But we also need to check if we should remove the gameplay effect.
So we'll check its removal policy.
So we'll say if.
Infinite effect Removal policy equals e.
Effect removal policy.
Remove on end overlap.
Now we've got some stuff to do, right?
So what do we want to do?
Well, first, we want to get the target actors ability system component because up here we're storing
the active effect handle and the target ability system component that we're applying this effect to.
We want to check that ability system component against our key value pairs in our map.
So first we have to get our target actors ability system component.
So I'm going to make a local you ability system component.
Called target ASC and use the you ability system blueprint library get ability.
System component passing in target actor and we'll check if that's valid.
We'll say if is valid.
Target ASC.
That way we'll only do this if it's not pending.
Kill and I'll say if not is valid return.
We'll do it that way.
Then we'll loop through our map and check if for each key value pair.
If there is an ASC linking an active effect to the ability system component that we're dealing with
here.
So we'll say for I'm going to use auto, we'll call this handle pair in active effect handles.
And if you don't like using auto here, the type is right here t tuple mapping f active gameplay effect
handle to you ability system component.
So you could just copy that if you're in writer or just hover over it in Visual Studio and see what
it is and then replace auto with that same thing.
Now what I'm going to do is take this pair, handle pair and see if the value which is an ability system
component is equal to my target.
ASC So we're going to check to see if they're equal.
We're going to say if Target ASC is equal to handle pair dot value.
If they're equal, then we can remove the gameplay effect and we remove it using the ability system
component of the target.
So we're going to say target ASC and to remove an active gameplay effect, we use the function, remove
active gameplay effect and this function requires an active gameplay effect handle.
How about that?
We have one.
It's in our handle pair.
That's what this map is storing.
It's our handle pair key.
So we're going to say handle pair dot key here and this removes the gameplay effect.
Now if we remove the gameplay effect, we should also remove this pair from the map.
But there's a problem.
We're looping through the map.
We can't remove an element from the container we're currently looping through that's just begging for
a crash.
Instead, what we can do is as we remove gameplay effects, we can accumulate some sort of container
of key value pairs that we'd like to remove after we're done with this for loop.
In other words, we can make an array, I'm going to make a T array and it's going to be an array of
the active effect handles.
So it's going to be a T array of F active gameplay effect handle, and this will be called handles to
remove.
And if we remove a gameplay effect using the handle, we're going to add that handle to handles to remove.
So we'll say handles to remove dot add and we'll add handle pair dot key.
And once we're done looping through this map, we'll loop through our array.
So we'll make another for loop.
This one can be auto reference, we'll call it handle and we'll loop through handles to remove.
And what are we going to do?
We're going to remove that particular element from our map by key because the key is an active effect
handle.
So we're going to take active effect handles and I want to do this safely.
So I'm going to do find and remove checked and we'll pass in handle.
And by the way, this auto here is just an active gameplay effect handle.
So if you don't want the auto there, you can explicitly state active gameplay effect handle there.
And so this results in removing the active gameplay effect from that ability system component that we
linked up with it when we added the key value pair of effect handles to assess here when we apply the
effect.
So on overlap will apply the infinite gameplay effect.
If the effect application policy is apply on overlap and on end overlap.
If our removal policy is remove on end overlap, then we'll check our map, see if it contains a key
value pair that has an ASC that matches the ASC of the end overlap actor.
If it does, we'll remove the active gameplay effect and mark that handle to be removed from our map,
which we do immediately after.
So back here in the editor, we need to make a couple changes, right?
We have to go to our BP fire area and change some of these properties.
For one, we don't have an instant gameplay effect class.
So instant effect application policy can be left at do not apply.
Same goes for duration.
Do not apply.
But we have an infinite effect and the infinite effect application policy needs to be set if we want
to apply the effect.
I'm going to apply on overlap and for infinite effect removal policy, I'm going to remove on end overlap
and we're going to remove a single stack.
Now, if we wanted to remove a single stack or all the stacks, we could create a variable and expose
it for that as well if we really wanted to.
All right.
And now the only thing we need to do is make sure to call our blueprint callable functions on overlap
and on end overlap.
That means we're not going to use our apply effect to Target anymore.
We're going to call on overlap.
Which takes the actor, We're going to pass in other actor and we're going to take our box and get on
component and overlap.
And for this we're going to call our end overlap function.
On and overlap.
And we're going to pass in other actor here into Target actor.
All right.
So what we should see is our infinite effect applied when we overlap and removed on end overlap.
So that's looking good.
So why don't we give this a try?
I'm going to press play.
I'm going to overlap and there goes my health.
It's going down.
And as I end overlap, it's no longer going down, which is exactly what I wanted to see.
I'm going to do this with show debug on I have 30 health, I'm in the fire.
It's going down 2015 ten and as I leave it stays at ten and this is looking great.
We can even check the stacking for this.
Let's just double check our stacking policy.
Looks like our stacking type is none, but we can choose to aggregate by source or target.
I'm going to choose target and set a stack limit count.
Let's do say three and stack expiration policy can be removed single stack and refresh duration so that
way we can have up to three stacks at a time.
And as we end overlap, we'll be removing those stacks.
So why don't we duplicate this a few times, We'll duplicate it twice.
So there's a point here where we might be overlapping with all three and I'll show debug and if we're
overlapping with one we should see our health going down by five at a time.
If we're overlapping with two, we should see it going down by ten at a time and with three we should
see it going down by 15 at a time.
So let's see if we can do that.
Okay.
We're going down by five.
And 35.
50.
65.
Okay, we're going down by looks like 15 at a time.
And as I move away.
Those stacks get removed.
So it's a little hard to see with such small volumes, but we get the gist.
I'm going to go ahead and increase the size of these volumes to make it a little easier to test out.
I'm going to take the box here and make its volume a bit bigger for this volume here.
And I'll just duplicate this.
Affect actors, so I'm going to go ahead and delete the others.
So we'll have this one.
I'll go ahead and duplicate it.
Like so.
And I'll duplicate it again like this.
So that way we have an area here where we'll have one stack, an area here where we'll have two and
an area in the middle where we can have three stacks.
So I'll turn off hidden and game.
Okay, so I'll press play.
I'll show debug and if I go into this area here.
35, 30, 25, 20.
So we're losing five at a time.
Now, I expect to lose ten at a time in this area and I have -40, -50, -60 and so on.
And as I go into this area, I should see -115, -131 6175.
So it's as expected.
Now here's the thing, though.
If I have three stacks and I leave one of these volumes, all of a sudden we no longer get any changes
to health.
We've removed all stacks, right?
And that's because removing the gameplay effect removes them all.
But what if we only want to remove a single stack?
Well, let's close the editor and go back to our IDE where we're calling remove active gameplay effect.
If I hover over it, I see that the first input is the active effect handle, but the second is an 32
called stacks to remove.
Now it has a default value of negative one, and if we don't pass anything in that negative one means
remove all stacks.
But if we want, we could remove a single stack.
So for example, I could place a comma here and we could simply remove one stack.
Now we may have multiple effect actors and each has their own map, right?
So each one should really just remove a single stack if we want that behavior.
And now we're set up to do that.
So let's hit debug and try this out.
All right.
I'm going to open my blueprint assets And to make testing really easy, I'm going to make my fire area
modifier magnitude negative one.
That way I'll know if I have one stack, two stacks or three stacks.
Let's give this a try.
So I'll press play.
I'm going to show debug.
I have 50 health going into this area.
It's going down by one per second now, overlapping two of them.
It's going down by two per second.
Going into this one, we have our health going down by three per second.
Now, if I go into the two area.
And we're now going down by two per second and going into the one.
We are going down by one per second.
So we're only removing a single stack at a time and exiting that area.
We're no longer losing health.
And of course we've long since gone past zero health.
We're going into the negatives which we shouldn't be able to do.
We're going to learn how we can implement clamping for these effects soon enough.
But for now we know that we can have multiple stacks of a single gameplay effect, and this allows us
to make any number of gameplay mechanics that apply effects.
So this is pretty great.
Now I'm going to go ahead and just delete these fire areas because what I'd rather have is an area in
which I don't start losing life unless I can see fire there, right?
We just made that box visible for debug purposes.
I'm going to turn off hidden in game or rather turn it back on and we can have multiple fire areas.
So say I have one here and one here and one here.
And that way we know that this is dangerous to be in and if we get out of it, we stop getting hurt,
right?
And we just happen to be able to stack these as well.
And you could make a super fire area if you wanted to by making the overlap volume bigger.
Let's just demonstrate that we'll take the box, we'll take the box extent and make it a little wider,
maybe like this and we can have multiple fire effects so we can add a Niagara.
Particle system component.
We can call this one.
Fire underscore two.
We can move it around.
Set it to fire.
We'll just have to for now.
I think that's enough.
And I'll make this volume.
Encompass the area.
So let's squeeze it in like that.
I'll make this fire effect right here.
And this one here.
Like so.
There.
And now we know if we step in the fire, we'll get hurt.
Excellent.
So our aura effect actor is quite versatile.
We can use it the way we used our overlap and end overlap functions and fire area.
Or we can use it like we did our health potions and just call apply effect to target directly.
We have control here in blueprint and it gives more flexibility to the designer side of things.
Okay, excellent job.
So now I'm going to give you a side quest.
Now these are optional, and I won't be giving you the solution for the side.
Quest I'd like you to create arrays of each duration type and figure out how to handle that and then
post your solution in the gas top down RPG channel in the Druid mechanics discord.
So instead of applying a single effect, you're going to apply an array of effects and make sure you
figure out how to handle removing all of those effects.
And if you can manage to get that working, post that in the Discord and discuss it with other students
in the course.
Now these side quests again are optional.
They're a little more challenging.
And as I mentioned, I won't be working through the solution for them.
This is more a practice exercise for you if you're up to the challenge.
And if not, that's totally fine.
Just continue with the course.
We won't be using anything in the side quests in the course, so it's not something you have to have,
but it's something that can get your brain working and get you to start thinking about how you can approach
solving problems on your own.
So if you're up to the challenge, tackle the side.
Quest and I'll see you in the next video.