Welcome back.
So we've created some damage effect params and we're using that now with our aura ability system library
and our ability system library when we call apply damage effect assigns some set by color magnitudes
for us.
And now we would like to use some of these that we're not using like our debuff parameters.
Now there's a place where we're determining combat phenomena.
That is an exact calc damage here In exact calc damage, we're determining all kinds of things.
We're seeing if there's a block, we're seeing if there's a critical hit, we're taking all these other
parameters into account.
Well, we can check here to see if we got a debuff as well.
In fact, we could do that before we even determine how much damage we're causing.
Right.
Because if we're getting a debuff, that just involves some calculations based on our debuff parameters,
we don't really care about damage.
So I'm going to do some calculations before we get into damage, and that's going to be to determine
whether there was a debuff.
So I'm just going to have a comment here that says debuff and we'll see what we can do here.
Now there are different debuff types and we know how many debuff types there are if we check out our
gameplay tags.
In our gameplay tags, we have damage types to debuffs, so we know the debuff types that there are
and our aura ability system library is setting our debuff chance damage, duration and frequency.
And we also know the damage type and we know that damage types are associated with debuffs thanks to
this map, damage types to debuffs.
So if we loop over this map, we know both the damage type and the debuff.
And that's what I'd like to do in exec calc damage.
So I'm going to make a for loop.
I'm going to loop over a T tuple.
That maps gameplay tags to gameplay tags.
I'm going to call this pair and I'm looping over gameplay tags and it doesn't look like I created a
gameplay tags const variable, so I'm going to say const ora.
Gameplay tags.
That's a const ref.
We'll call it gameplay tags.
And we'll call for gameplay tags get now that we have that singleton.
I can loop over that map.
Damage types to debuffs.
Now for each damage type slash debuff, what are we going to do?
Well, if we have the gameplay effects spec and we do it's called spec right here, we can always get
set by caller magnitudes.
So if I took my spec and I called get set by caller magnitude passing in a damage type, we would get
a magnitude because our ability system library is setting the damage type and it's setting a base damage
associated with that damage type.
So if the set by color magnitude has been set for this particular damage, type in the pair in our for
loop.
We know that this is the damage type we're dealing with.
Now, how do we know which damage type was which?
Well, remember, get set by caller magnitude has a default if not found parameter.
So if we don't find a given damage tag.
As a set by color magnitude, we could default to something, say negative one, because we'll never
be assigning negative damage.
At least that's a convention that I'd like is never to assign negative damage.
It doesn't make sense if we're going to.
He'll go about it a different way, but don't assign negative damage.
So what we can do here is get a set by color magnitude for the damage.
Gameplay tag and damage types to debuffs has damage types as its keys so we can take a key.
Now.
I don't want to warn if not found, because there will be one damage type we do find and other tags
we don't find.
So I'm going to pass false in so we can suppress that warning.
That way we don't have to go and do like we did in blueprint and set set by color magnitudes for all
of them and just have zero.
Right?
But also, though, there's another reason to not set set by color magnitudes for the tags we're not
using because I'd like to use this default if not found.
If not found, I'd like this to be a negative one dot f.
In other words, get set by color magnitude, which returns a float is going to return negative one.
If we don't find the damage type as a set by color magnitude.
So what I can say is float, we'll call it type damage and this could be a const float and this float
will be negative one for any damage types that don't exist as a set by color magnitude and our spec.
So we can use that to prevent doing anything else we can say if type damage.
Is greater than negative one.
F.
Right.
So even if we do set our damage, even if damage is zero, we'll get a negative one here, in which
case we should do nothing.
Now, if we make it past this, then we know that our pair key is the damage type that we're interested
in.
In fact, we could just say const f gameplay tag reference damage type here.
So we know that pair key is the damage type.
And we can make a const gameplay tag.
Reference called debuff type and that'll be the parent value.
So this makes it clear which one is which.
And that way we know right here we're getting the set by color magnitude for damage type.
This is more clear.
I like this better.
Now, if you're worried about floating point in precision, perhaps get set by color.
Magnitude doesn't find it and it returns a negative one.
But we know that it's not going to be exactly negative one.
It could be plus or minus some amount.
Well, you could just be safe and make this -0.5 or something like that.
Now, if you did this, it looks weird.
So you have to say 0.5 padding for floating point M precision.
And as long as you don't go using a negative value between 0 and -5, you're fine.
But negative one should be fine.
So anyway, now we know the damage type and we know the debuff type.
More importantly, this is what we're really interested in.
And if we know the debuff type, then when we calculate our debuff chance we know what kind of debuff
we should be applying.
So here we're going to determine if there was a successful debuff.
How are we going to do that?
We're going to get our debuff chance, our debuff damage, our debuff duration and so on.
So let's make a const float source debuff chance.
As this is on the source, after all.
And that's going to be gotten from the spec.
We're going to get set by color magnitude and we need the debuff chance gameplay tag.
So we're going to use gameplay tags dot debuff chance.
We don't want to warn if not found and we'll use negative one dot f if not found.
Now we need to determine if there was a debuff.
Right.
Well what if we'd like to see if there's some kind of resistance calculation if we scroll up, notice
that we do have these resistances.
And we even have a tags to capture deaths to map that we could search for any resistance tags.
We know that matches tag checking against attributes.
Resistance would get us any of those tags so we can check against resistances here.
So if we know our damage type, we know the resistance tag thanks to F or a gameplay tags.
And if we know the resistance tag, we know the capture def thanks to our tags to capture def map.
So let's say we'd like to capture the resistance corresponding to this damage type.
How would we do that?
Well, we make a non const float called target debuff resistance and set it equal to zero.
And then just like when we captured our other attributes, we take our execution params and call attempt
calculate captured attribute magnitude.
This requires an gameplay effect attribute capture definition.
So we're going to get that from damage statics.
We're going to get tags to capture deaths and we're going to index the resistance.
We need to know the resistance.
Why don't we go ahead and just make a gameplay tag local variable for the resistance tag?
We're going to say resistance tag and that's going to be something we get from gameplay tags the singleton
because we have damage types to resistances and we can index the damage type.
And if we have a resistance tag, we can use that in tags to capture deaths and this will return a capture
def.
Once we've done that, we have to pass in evaluation parameters and we have to pass in the float target
debuff resistance.
So now we know the resistance to the damage type for this particular debuff.
So they're all connected, right?
Debuff type damage type resistance.
These are all corresponding.
There's a debuff type and a resistance to fire.
Its debuff type is burn and so on.
So now that we have the target debuff resistance, if we want we can make sure it's not a negative value.
We can say target debuff resistance equals f math max float, target debuff resistance and zero.
And then once we've done that we can calculate our effective debuff chance.
So we need to see what is our effective chance.
What do we need to hit in a die roll on 100 sided die?
Well, we're going to make a const float called Effective debuff Chance.
And we can decide on a math formula.
Well, we know that we have the source debuff chance.
We can multiply this by a factor and we can do what we've done before.
We can take 100 minus target debuff resistance and divide this by 100.
Now this means that each point in our resistance takes off literally 1% of the debuff chance.
If you want to scale that by a factor and even drive that by data such as a curve table, you know how.
But I'm okay with each point of resistance reducing our chance to get a debuff by 1%.
Now we need to know if we got a debuff, so we're going to make a const bool called B debuff and we
roll the 100 sided die.
We say F math randrange.
Between 1 and 100.
We see if that's less than our effective debuff chance.
Now we know if we got a debuff we can check if b debuff.
Now, the real question, right?
What do we do in this case?
If we know we should have a debuff.
What exactly do we do about it?
That we're going to handle next?
But before we wrap up, we just added a whole bunch of lines to an already big function, and I don't
really like the function being that big, so this could be abstracted away into a function.
We can refactor this.
So if I extract it, then we see that this has to take in three parameters.
It has to take in execution params, it's automatically going to be a const reference, which is perfect.
We need the gameplay effect spec, which can be a const reference which is perfect and we need F aggregator
evaluate parameters and also f or a gameplay tags.
You know what we don't need to pass in gameplay tags do we?
So I'm going to cancel this and just subsume that line to.
And go back and refactor extract method and now we only have three input parameters.
That's all we need.
We'll see that it does all of these lines of code here.
So I'm going to go ahead and give this a name and it's going to be determine debuff.
We'll click next.
And now we have this nice function determined debuff that determines the debuff.
Now the question though is still what does determine debuff do?
Where is it?
It's up here.
Right here.
Let's put a big fat to do.
We'll say, What do we do?
Isn't that a good to do?
To do.
What do we do?
Well, it's like to be continued because we'll be figuring that out in the next lecture.
For now, it's enough to know by the time we've gotten to the exit calc whether or not we have a debuff
based on the debuff chance.
And we know that our firebolt has a, I think 20% debuff chance.
So what we could do is we can run in debug mode with a breakpoint right here and see what that debuff
chance is.
In fact we can see if we got a debuff.
All right.
So I'm going to press play.
I'm going to launch a fireball.
And let's see what we get down here.
Now, this is interesting.
Before we hit our break point, we actually hit an exception.
And I'm seeing the problem now.
It's something that I missed earlier on.
You see tags to capture deaths is a member variable on the aura damage statics class.
But as we can see, there's only one element in this map at this point in time, so it's too early for
us to access anything from it.
And that's a problem.
To make this easier for us, I'd like to just make a local team map in the execute implementation function
and just fill it in there and use it there.
So here's what I'll do.
I'm going to make a T map locally and execute implementation.
I'll do it first thing.
It's going to be a T map mapping F gameplay tags to F attribute capture definitions.
They're actually F gameplay effect attribute capture definitions.
And this is going to be tags to capture deaths.
And I'm going to fill this in here.
So what I did up here in the constructor for Aura Damage Statics, I'm going to do that down there.
I'm going to go ahead and cut this out and I'm going to remove tags to capture deaths from aura damage
statics altogether.
We plan on just having a static object of this type.
And down here in execute implementation, I'm going to paste those lines.
But instead of passing in armor def, I need to use damage statics dot capture def.
So we'll get that from the static or a damage statics and we'll go ahead and do that for all of them.
And that way we're just making a local team map just for our usage here.
And then I don't have to go and call the Aura Damage Statics constructor.
We should only be going through the damage statics static function.
So everywhere where calling aura damage statics, I'm going to replace that with damage statics.
Except here it no longer has tags to capture defs, so I'm just using my local tags to capture defs
here.
And the same thing here.
I don't need to get it through or a damage statics.
I'm just going to use the local variable.
So let's see where else we're using that constructor.
I think that was the only place.
So now that we have our team map local to execute implementation, I'm just going to pass that in to
determine debuff.
I'm going to pass in a team map with this signature.
So back in exec calc damage for determine debuff.
I'm going to add one last parameter, but it's going to be a const reference.
That way we don't go making copies of this thing and it's going to be tags to defs and we'll make it
in tags to defs.
And that's the tags to capture defs.
We'll go ahead and add that here and we'll add it in the CPP file for this function.
And I'll go ahead and stop debugging while I'm at it as I'm making changes here.
So now determine debuff takes in tags to defs.
We can use this instead.
So right here, instead of getting it from damaged statics, we're just going to use in tags to deaths.
Now, in addition to that, when we call determine debuff, we need to pass in tags to capture deaths.
So now we can go ahead and test this out.
And I don't expect to see an exception get hit this time.
So now we can do some damage.
But one other thing that I just remembered is that our enemy's secondary attributes, gameplay effect
doesn't really have any resistances.
So I'm going to add a resistance modifier for fire resistance just for testing purposes in GE.
Secondary attributes enemy.
So I'm going to go ahead and collapse all these modifiers and add one more.
And this last one is going to use fire resistance.
I'm going to override fire resistance and set it to a value of, let's say, 20.
And now I'm going to fire a firebolt.
And now we don't hit that exception.
We hit our break point, which is what we always want to see.
Right.
Now our resistances tag is fire and we're checking in tags to deaths, which has ten captured attribute
deaths inside of it, including our resistances.
So we're getting our attribute magnitude, which we expect to be 20.
We're putting that into target debuff resistance and here we see it's 20.
So we got that target debuff resistance.
We're calculating an effective debuff chance where the debuff resistance is 20 and the source debuff
chance is also 20.
We're multiplying that by 100 -80 over 100, which is 80% of that.
That gives us 16.
So rolling the 100 sided die.
Do we get less than 16?
No, we don't.
But at least we see that effective debuff chance is correct.
So now we have something that works.
We're not doing anything that we don't realize we're doing and things are looking good.
So with that we can go ahead and wrap up.
I'm going to resume, but before we head on out, I'm just going to make sure our secondary attributes
has our resistances and I can take a look at how Aughra's resistances are set if we go to effects secondary
attributes, these are the ones aura uses.
Aura should have her resistances so we can collapse all the modifiers.
Notice there are much more of them for more to be exact.
And let's see how they're set.
It says there override modifier magnitude is attribute based and we have a coefficient of 0.5 a post
multiply additive value of three and let's just see if our life will be easy if they're all the same.
Yeah, it looks like they're all the same.
So we can just add these modifiers over to secondary attributes.
Enemy.
So the fire resistance, one can be override, but we can set it to attribute based.
And what is the backing attribute for fire resistance?
It looks like it is.
Resilience.
And I think they're all resilience except for perhaps physical.
Physical resistance is backed by.
Resilience as well.
They're all backed by resilience.
That makes it easy.
So let's go ahead and just add those to our enemies secondary attributes.
And that way we can test the other resistances when we get to those.
So attribute based are attribute based magnitude is going to have a coefficient of 0.5, a post multiply
of three, and the backing attribute will be resilience on the target.
And we're not snapshotting.
So I'm going to time lapse adding the other resistances now.
Okay, so now our enemies have secondary attributes and we could add that to their test gameplay ability
as well.
But that's fine.
We'll do that when we need to do that.
For now, we know that they have resistances and we can test them.
The only thing left to do is to decide what happens if we get a debuff right.
So in determine debuff we have this boolean.
I'm going to remove that breakpoint, but we have a to do.
What do we do when we get a debuff?
That's what we're going to handle next and we'll see what that involves.
So great job and I'll see you in the next video.