Original Post — Direct link
over 2 years ago - /u/Mark_GGG - Direct link

Originally posted by daman4567

I mean, "unaffected by chill" doesn't mean "chill effect on you is 0%" necessarily. It essentially means "chill doesn't affect your action speed".

I mean, "unaffected by chill" doesn't mean "chill effect on you is 0%"

It does mean exactly that.

over 2 years ago - /u/Mark_GGG - Direct link

Originally posted by deddead3

Tl;Dr: given how slayer leech works, we know ailments still have values regardless of if you're affected by them or not.

Based on what the other commenter said and what I would assume given my own experience as a dev (not poe, of course), "unaffected by chill" is boolean on the mob used when it walks through its list of ailments to apply for a given server tick. It basically says "do I apply the effects of this ailment?" The ailment itself is still in the list of ailments on you.

It's the same thing as slayer's "unaffected by bleeding while leeching". The value of the bleed is still there in the background, you just don't take damage from it until you stop leeching. If you muck about with different sized phys damage for self bleed and leech duration, you can notice different levels of bleed when you start taking damage from it again.

Ie: 3 second, 1m/sec bleed vs 4 second, 100/sec bleed. If leech runs out at 1.5 seconds, you'll probably survive the latter and not the former.

we know ailments still have values regardless of if you're affected by them or not.

Buffs and Debuffs (including but not limited to ailments) have an internal magnitude for each stat they apply to the thing they're on. This can be thought of as the "base" value for that effect of the buff/debuff. The actual effect of a buff/debuff is adding amounts of stats to a target. The amount of stat a buff/debuff applies is determined by it's magnitude, modified by any modifiers to it's effect. There are several categories of such modifiers:

  • The source of the buff can apply "increased"/"reduced" or "more"/"less" modifiers to the effect of the buffs/debuffs they create. In general these are locked in at the time the buff is created, because after being created a buff is independant of it's creator, linked only to its target - the thing the buff/debuff is actually on. So if you chill something, and then gain "x% increased Effect of Chil", the chill you already applied doesn't retroactively update.
  • The target of the buff can have "increased"/"reduced" or "more"/"less" modifiers to buffs on them. These are dynamic - if you get chilled, and then gain "x% reduced effect of Chill on you", that will change the effect the chill is currently having on you - the amount of "reduced action speed" it's adding to your stats.
  • The target of the buff can have modifiers making it "unaffected" by the buff/debuff, which means the effect of that buff/debuff on them is 0 - it doesn't change their stats at all.
  • The type of buff can impose restrictions on how big the effect can be - chill's effect is limited to the range of 0-30.

The magnitude (internal to the buff) doesn't change (in normal cases). The effect - the amount the buff changes stats on the object, calculated from that magnitude - does. But the effect is the part which is actually on the object and thus can change it.

The stat chill applies on the target is "x% reduced action speed from chill". It's effect is to add a value (locked to between 0 and 30) to that stat on the target. If the target is unaffected by the chill, then the value is 0. Having this stat reduces action speed.

Bonechill works by making the calculation for increased damage taken on that target check the value of that chill stat (this is one of the reasons chill has its own dedicated stat instead of using a generic action speed reduction stat), and include that value in the result. So if the target is unaffected by the chill, Bonechill doesn't achieve anything, because the stat value is zero.

used when it walks through its list of ailments to apply for a given server tick.

For the record, nothing similar to this actually happens, and for good reason. That would be extremely computationally expensive and make the game very very slow.

When a buff is added to an object, it applies changes to that object's stats.

When a buff is removed from an object, it removes those changes.

If a buff's effect on a target changes (because any of the above modifiers applied or because a stronger buff of the same type went away and let it start having an effect), the stat changes from that buff are recalculated (in practice, removed and then reapplied at the new values for most cases).

Stuff the object does only needs to check the object's current stat values.

This is because fundamentally, the server ticking happens much more frequently than buffs being added/removed/changed. So doing the work only when those things happen rather than constantly looking through buffs/debuffs on a target every frame and recalcualting which ones are applied would be horribly inefficient.

over 2 years ago - /u/Mark_GGG - Direct link

Originally posted by EchoLocation8

When a buff is added to an object, it applies changes to that object's stats.

When a buff is removed from an object, it removes those changes.

I've tried building a bit of a game engine myself, and one of the things I felt like I was doing wrong was my handling of this, or more specifically, timers.

If you'd be willing to share, how does the engine know when its time to remove the buff from an object?

What I designed was essentially creating a Timer object that you could set a number of milliseconds to, and each Timer was an Updateable object that subtracted the frame time from the time remaining on the Timer object, when it hit 0 it would clean itself up and drop whatever effect it had on whatever it was attached to.

Like, how off-base was I there? From a scale perspective I just felt like, if I had thousands of these timers going for buffs/debuffs/dots what have you, I guess in my head it just felt slow and inefficient but I couldn't figure out a better way to manage them. And I don't even know if it would be bad or if it's another classic case of "computers are actually pretty fast and doing this is fine".

PoE does something like that in a general sense, yeah. Objects have a list of buffs/debuffs on them, and each buff/debuff has a variable for time left, and they get iterated to subtract from it each frame. Provided the structures for holding them are fast to iterate and you're not doing much on each other than that subtraction this isn't a huge deal. And it is flexabile - we can have some buffs see time passing at a different rate (such as with Temporal Chains) by manipulating how much frame time you tell each one has passed - but like with effect, we don't calculate that modifier every time, we calculate it when something affecting the result changes and store the result in the buff to be used without needing to recalculate each frame.

Depending on the requirements there might be other options. I'm coming up with this on the spot, but as an example if you're willing to commit to keeping the list ordered by how much time each buff has left, that adds cost to adding new ones since you need to find the correct place, which is O(n). But then you effectively only need to track time for the first thing in the list, and then when it's time runs out, you remove it and subtract it's fulll duration from each other thing in the list - so you're only iterating the whole list when one runs out, because you know the first one in the list will be the first to be removed, and everything behind it just tracks being around for that one's duration when it's removed (when adding a new buff to the list you'd also need to add the amount of already-exipired duration on the front one to the new one's duration, to account for it not having been around for that part, but that's inexpensive). We wouldn't do this for PoE (couldn't do the afformentioned temporal chains stuff, for one thing), and it has costs as well as benefits, but if the majority of buffs in a game have the same duration (and thus you can optimise insertion by starting from the back of the list since thats where most will go), and you're looking to keep the system fairly simple and don't to do things this makes harder, that system or something like it might be worth considering.

There's rarely a best way to do a type of system, only best ways to do the specific version of the system for a specific game (and that can change over time).