Resistances and Defenses

Published June 22, 2018
Advertisement

I've just come off a several-months-long jag of playing Path of Exile. PoE has influenced the (sporadic) development I've done through that time on Goblinson Crusoe by a great deal. While GC is turn-based, it shares a lot of the same DNA as PoE, specifically in the influence of Diablo and Diablo 2, so a lot of ideas and mechanics from those games have been seeded throughout GC.

Like Diablo 2, Path of Exile implements a system of resistances for elemental damage (fire, ice, lightning). Resistance stat values are obtained primarily through gear, and are obtained as percentage values that accumulate up to a maximum value. For example, you could get a belt with +35% lightning resistance. Resistance amounts from gear and other sources are accumulated, then capped to a maximum value (75% by default), with the option of obtaining small increases to this maximum value via other sources. Resistance either reduces or increases (it is possible to have negative resistances that actually boost the damage the player takes) the incoming damage by the given percentage. 

As one progresses through the game, at certain checkpoints the player's resistance value has imposed upon it a penalty. In the beginning, this penalty was imposed in smaller stages as one progressed through the difficulty levels. (Difficulty levels simply repeat the story of the game, with higher-level monsters as well as the resistance penalty coming into play.) In current PoE, the penalties are imposed at two checkpoints within the story: the first after completing Act 5 (character level approximately L45) and the second after completing Act 10 (around 67 to 70 character level). The first checkpoint applies a -30% reduction to resistances, and the second checkpoint another -30%, for a total of -60%.

I understand the thinking behind this design. The game is balanced around having maximum resistances. That is, any given encounter will be damage-tuned with the assumption that the player is at resistance cap, and thus not having the resistance value at cap can bring extra punishment and pain. This provides a constant pressure for equipment improvement as one progresses to end-game; gear that was fine before the checkpoint now is suddenly deficient, pressuring the player to seek upgrades. At a certain point, though, the player can obtain enough +res% to overcome the penalties and still raise their resistance to 75%, meaning they are effectively "done" with upgrading their resistances. (Further equipment upgrades for other stats must be chose to maintain these resistance caps, but that is usually not too difficult.) Typical players are encouraged to obtain these caps as soon as possible to ease the leveling and progression process.

While I understand the design, I've always been bothered by the implementation. Having gear that was "fine" at one point suddenly become "totally deficient" in one instant after beating a single specific boss feels too abrupt. Also, I kinda don't like that at a certain point the pressure to maintain resistances eases up. So in GC,  I am exploring ideas for putting this resistance penalty system on a smooth curve, rather than having the abrupt steps. The current iteration of this system uses a logistic function, which is a type of sigmoid function. Instead of collecting gear that provides +X% resistance to a given damage type, you collect gear that gives +Y resistance rating. This resistance rating is plugged into a logistic function to obtain the actual amount of resistance % to apply against incoming damage. The logistic function is structured like this:


function res(rating, levelbase, levelslope)
	return (1.0 / (1.0 + math.pow(e, -levelslope * (rating - levelbase)))) *2.0 -1.0
end

The function operates using the rating (granted by equipment and other buffs) as well as a base rating for a given level, levelbase. At level M, if the player's rating is equal to level base, then the granted resistance value will be 0%. Rating less than levelbase results in a negative resistance, while greater than levelbase grants a positive resistance. The factor levelslope is used to affect the spread of the function at a given level; ie, how quickly the resistance approaches 1.0 or -1.0. For example, if you use a levelslope of 1, that means that the resistance value will be very close to -1.0 at a rating that is 6 points below base, and will be very close to 1.0 at a rating that is 6 points above base. This slope value determines the slope of the curve where it passes through the origin of the graph. By making the slope shallower as the character level increases, that spread can be made wider, granting a larger window within which the rating will grant a resistance value somewhere between -1 and 1. This way, as resistance ratings grow larger, the absolute difference between the rating and the levelbase has a more gradual effect on the value. These graphs show how this works:

WsxfEEM.png

At a levelslope of 1, you can see here that around 6 points below the levelbase, the curve approaches -1, and at around 6 points above, it approaches 1. So if the base resistance rating for that level were, say, 10 then if you had a rating of 4, you would have a resistance value of -100% (or close to), meaning you would effectively take double damage, whereas if you had a rating of 16, you would have a resistance of 100%, meaning you would take no damage. Now, at a higher level, you might have a levelslope of, say, 1/3:

VK4wFrX.png

Here you can see that the spread is now approximately -16 to +16 from level base. If the levelbase rating for that level were, say, 100 then if you had 84 rating or below you would take double damage, whereas if you had 116 or higher you would take no damage.

Of course, the base and slope ratings would be a candidate for a great deal of tuning in the final game. The constant increase of levelbase applies constant pressure for the player to upgrade resistance, not simply at 1 or 2 main checkpoints, but all throughout the game, with that pressure growing larger the longer one plays and levels up without changing equipment. And this also doesn't account for having a resistance cap. In PoE, the default cap is 75%, which can be raised only through rare and special means, which is a sensible design decision in my opinion. A simple solution for this would be to multiply the output of the resistance function by the value of the cap if the output is positive (leaving the negative resistance value uncapped). Doing it this way, the positive side of the curve approaches the resistance cap, rather than 1.0, while the negative side is untouched. I could even implement a negative resistance cap, if so desired, to allow the player to build a stat to reduce the amount of damage taken from having a negative resistance.

In my preliminary tests (which include no playtesting so far by anyone but myself) it seems to work fairly well, but this is one of those kinds of systems that I will need to tinker with and explore more fully in the final testing phases. Just because it works well now, doesn't mean it won't be massively exploitable in the future.

 

I have also been tweaking and experimenting with damage types. At the moment, I have a system of damage types somewhat similar to PoE, though with quite a few differences. In my system, any given attack or spell can deal a certain combination of damage types selected from the set {Crush, Slash, Burn, Poison, Bleed, Void, Shock}. These damage types can additionally be tagged with modifiers drawn from the set {Projectile, Melee, Attack, Spell, Area, DoT, Siege} which can be used to apply damage increases or reductions. So as an example, a basic fireball of some sort might deal 2 damages. The first would be tagged {Crush | Area | Spell} and the second would be tagged {Burn | DoT | Area | Spell}. Player stats exist that can amplify or reduce damage dealt by any of these various tags. So, for example, it might be possible to have a stat that increases Spell damage by 13%, meaning both damage rolls from this fireball spell will be boosted by 13%.

Primary damage types all come with a secondary debuff effect. Crush causes a stun/slow effect, slowing the target by some amount for some period of time. Slash damage causes a Bleed debuff that causes damage over time (this damage bearing the {Bleed | DoT} tags). Poison damage also applies a stacking debuff to poison and burn damage resistance rating, meaning that poison and burn damages become more potent the more poison stacks there are. Void causes an increased chance to take a critical strike, and Shock increases all damage taken by a certain %. These effects are all subject to change as the game develops further. The idea, though, is that each damage type should be differentiated by a mechanic, and not just by a type. I've played games where there was no mechanical difference between, ie, Fire and Lightning, merely cosmetic differences and the necessity of maintaining resistances against two types instead of just one. If a damage type doesn't lend itself to some mechanical difference from the others, then it will be altered or removed.

 

At the moment, all damage types are mitigated in a similar manner, using the damage resistance calculations describe earlier. That is, 'physical' types such as Crush and Slash are not mitigated using any kind of armor rating, but instead are mitigated by Crush or Slash resistance rating granted by certain equipment. Homogenizing the various damage mitigation strategies in this manner vastly simplifies the design of the character combat back-end and balancing, though again it is subject to change in the future.

1 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement