Saturday, July 22, 2017

[Alliance:HotS] Gear Set Bonuses

One of the things that makes Alliance: Heroes of the Spire so deep is the serious amount of gear customization you can do. Six gear slots, with set bonuses for having matching pieces that can seriously change how you'd deploy that Hero, and then slotting in gems, as well.

Gear set bonuses come when you equip either 2 or 4 pieces of a given set. The 2 piece sets are generally raw stat bonuses, whereas the 4 piece sets are the interesting effects:
  • 2P Bone; +20% HP
  • 2P Wraithbone; +25% HP
  • 2P Furyborn; +20% Power
  • 2P Dragonfury; +25% Power
  • 2P Coldsteel; +15% Armor
  • 2P Icesteel; +20% Armor
  • 2P Sharpthorn; +10% Crit
  • 2P Elderthorn; +20% Crit
  • 2P Brightshield; +15% Block, +5% Reflect on Block
  • 2P Sunshield; +25% Block, +7% Reflect on Block
  • 2P Nightleather; +20% Aim, +5% Armor Penetration
  • 2P Voidleather; +25% Aim, +10% Armor Penetration
  • 2P Bloodstone; 15% Lifesteal, +10% Healing Received
  • 4P Ironclaw; 35% Counterattack Chance
  • 4P Swiftsteel; 40% Chance of Bonus A1, +50% Crit (Damage Only)
  • 4P Wartech; 50% of Crits will have +200% CritMult
  • 4P Witchstone; Buffs/Debuffs can Crit
  • 4P Lifesilk; +30% Healing Done, HoTs can Crit
  • 4P Titanguard; 15% Less Direct damage taken, Redirects 30% of party damage to self
That is seriously a lot of options. Today's post is going to be looking mostly at the 4 piece sets, though I may talk a bit about the 2 piece sets in relation. How much does each set help? When might you use each 4 piece set? Let's start with some of the easier to discuss sets.

+30% Healing Done, HoTs can Critical Strike

Lifesilk is pretty straight forward: you want this unit to do more healing. The details are the +30% is multiplicative (so if your Pyrus heals for 30% health, with Lifesilk he'd heal for 39% health), and critical HoTs heal for 15% of maximum health instead of 10% for the basic version.

Since critical HoTs require critical strike, you may want to pair this set with things that increase your critical strike, but if the unit doesn't have any HoTs, then it'll go well with literally any 2P stat increase. On the other hand, for high-level arena, you may end up using a more defensive 2P for your healers to survive. My Anat, for example, runs Lifesilk and Sunshield so she can have the extra block.

Buffs/Debuffs can Critical Strike

Witchstone is possibly the most complex of the sets, because what does a buff or debuff critting even mean? You can find an exhaustive list on the Alliance website here, but here's the rules of thumb to remember:
  • If the buff/debuff has a number, crits increase it (ie: Armor Break goes from -50% armor to -75% armor)
  • If the buff/debuff doesn't have a number, crits make them unpurgeable (Stuns, Sleep, Silence, Debuff Immunity, etc.)
  • Bombs are the odd one out, on a crit they stun when they explode
  • Bar drains and Bar fills are affected multiplicatively
  • Witchstone cannot cause HoTs to crit
Witchstone makes a good choice if you have buffs or debuffs you want to supercharge. Unpurgeable silence or stuns can do wonders in Path of the Ancients or some Lost Dungeons where one unit constantly cleanses their team. If the unit you're bringing is less about damage and more about control or support, Witchstone may make for a good choice.

An example here is Sunslash, the Order Sabretooth. He doesn't do much damage, but making his AoE Mark a Critical Mark effectively increases your team's direct damage output by 15.4% (130% extra damage for basic Mark to 150% extra damage for critical Mark).

15% Less Direct Damage taken, redirects 30% of party damage to self

Titanguard is a fun one, and in high level arena you can often see it appear in what seems at first glance weird places. The first benefit of Titanguard is a straight-up 15% damage reduction on Direct Damage. The damage transfer effect is not direct damage, nor are DoTs or damage reflect, so they would not be affected by this damage reduction.

The other benefit is redirecting 30% of direct damage taken by other units to the Titanguard unit. Again, direct damage, so DoTs, damage reflect, or other Titanguard transfers do not count. This will often alow some of your squishier units to survive that much longer, even if you don't have a taunt up, or allow them to survive against AoEs.

This does create a weakness in Titanguard units: it's often easier to kill them indirectly by piling on damage on other units, especially if said other unit has a lot of HP but not a lot of armor--Petra comes to mind here. Titanguard units may also be susceptible to teams that have a lot of high-power AoE attacks for a similar reason: 3 units' worth of damage redirects at once can drain the Titanguard unit's HP bar very quickly.

Finally, multiple Titanguard teams work interestingly: the 30% damage redirect is calculated first, then split among all available Titanguard units. So if you have 2 Titanguard units in your team, each one will take half (15%) of the redirected damage. You also cannot redirect damage to yourself, so if a Titanguard unit is one that's hit, they're not considered an available Titanguard unit in the damage transfer calculation.

Often you'll want to ensure Titanguard is on a unit that has a lot of health to begin with, since the damage transfer cannot be mitigated. Shields will still absorb it, but nothing else reduces it. So units like Petra, Valorborn, or even the Mechanics are good choices for Titanguard.

35% Chance of A1 Counterattack

Ironclaw and Swiftsteel are relatively similar in a mechanical sense. They both give you more (automatic) uses of your first ability. The difference largely lies in the trigger mechanism. Ironclaw requires you to be attacked in the first place. This makes Ironclaw a good fit for units that get attacked often: taunters, provokers, and guarders.

Ironclaw is effectively a DPS increase, but it can also be useful if the Hero has a debuff on their A1 you want to apply as often as possible. Gaius' A1 stuns, for example. Otto's A1 hits like a truck. Both good reasons to bring Ironclaw to the table. In the case where you want more debuffs, you'll likely want to pair it with a surplus of Aim. Especially for tanks, going mostly Aim instead of Block feels weird, but if the enemy team is mostly Stunned anyhow, it's not a big deal.

Ironclaw on farmer units that have self-healing (i.e.: Otto, Petra) is ridiculously effective since they're always getting attacked.

Note that you cannot Counter more than once per attack, so if your Hero already counters as part of their kit, Ironclaw is not likely a great choice.

It's a pretty straightforward ability, but look lower in the post for an analysis of Ironclaw vs. Swiftsteel, because that's where things start to get muddy.

40% Chance of a Bonus A1 followup, +50% Critical Strike for Damage

Swiftsteel is a bit more complex than Ironclaw. Every time you use an ability, any ability, you have a 40% chance of following up with a Free Attack, which is a usage of your A1 against the target. If your target is friendly, or the ability has no target, the Free Attack will choose a random enemy, ignoring Taunt or Provoke.

Rallies and Counters count as ability usage for Swiftsteel procs, so you can get bonus A1 attacks on those. However, the Free Attack cannot proc another Free Attack--but a Free Attack proccing a Counter on the opponent to proc a Counter on yourself and then Proccing a Free Attack off that Counter can occur. It's rare, but when it happens you basically just watch the two units hit each other over and over until one of them dies or someone doesn't proc a Free Attack. It behaves like a bug, but it's permissable under the rules of Counters/Free Attacks.

Swiftsteel also provides a +50% Critical Strike, but for damage only. Heals do not benefit from this. This means you'll often pair a Swiftsteel set with either a +Power or +CritMult weapon, rather than the typical +Crit weapon many go with.

The reasons for going Swiftsteel are pretty well the same as going for Ironclaw: it's a DPS increase, and if your A1 has a great effect, you may want more of those. Midorimaru (or any Samurai Cat) is a great case for Swiftsteel to spread more DoTs, for example.

Look below for an analysis of Swiftsteel vs. Ironclaw, and Swiftsteel vs. Wartech.

Half Critical Strikes are Supercrits (+200% CritMult)

Supercrits. The name sounds awesome, but what is a Supercrit? It's a Critical Strike that has an extra 200% CritMult applied to it. So for example, if you normally have 50% CritMult, a Supercrit will actually do 250% extra damage instead of 50% extra.

Unless you're rocking a surplus of Crit gem slots, you'll almost always want to pair this with a +Crit weapon.

It's ridiculously straightforward, and basically, if you need burst damage, Wartech is pretty much the way to go. But you also can't rely on it. On average it's really only increasing your CritMult by 100%, since only half your Crits will have it applied. So big, bursty, swingy damage.

Often units with big AoEs will get Wartech applied. If you're not terribly enamored of your Hero's A1, Supercrit may be the way to go for a damage increase. But how does it compare versus just 2 Dragonfury sets (+50% Power)?

For the sake of simplicity, let's pretend everything else is the same: stat allocations, etc. 100% Crit, no extra CritMult.

If we do 100 base damage, at 100% Crit with a 50% our base damage is 150.
With +50% Power, our base damage changes to 150, which after a crit is 225.
With Supercrits, our base damage is still 100, but a Supercrit is +250% damage, which is 350. But the floor half the time is 150. So an average damage of 250 instead.

So you can see that Wartech increases the average damage dealt a bit, even over +50% Power, but it's swingy. Sometimes you'll do way less, sometimes you'll do way more. If you have less than 100% Crit, the benefits of Wartech also go down. In this particular instance, you need 75% Crit to make Wartech do the same damage on average as 2 Dragonfury sets. Now, even if the average damage is lower on Wartech, it will still have a higher maximum. The maximum damage would still be 350. You'll just see it less often.

The above only holds for units that scale 1x with Power. If they have abilities that scale better with Power, then that 75% Crit inflection raises further. If their abilities scale worse with Power, then the Crit threshold goes down.

Enemy teams that have healers--Magitek Bards and Unicorns especially--basically have a HP reset button every few rounds, so burst becomes very important when fighting those teams, making Wartech attractive.

Ironclaw vs. Swiftsteel

Because they're so similar, Ironclaw and Swiftsteel may be an interesting question of which to use.

To ensure the same number of potential A1 procs a round, an Ironclaw unit would have to be attacked 1.14 times a round. However, Ironclaw is always against the attacking unit, whereas Swiftsteel is against the unit being attacked (usually), or a random unit if none is targeted.

Swiftsteel, if you get really lucky on buff procs, can wreck backline units as it ignores taunts. It's not an effect you can count on however, as you'd have to proc it at 40%, and then randomly select the backline unit (25% chance if nothing else is dead), so basically, if you use a buff with Swiftsteel, you have a 10% chance to hit a given enemy unit. It can be deadly to the opposing team, but not something you can build around.

Every unit in the game has an A1 that will end up having to target a Taunter, so if you have an Ironclaw Taunt up, eventually they'll attack your taunter, and you have a little bit better than a 1/3 chance to hit them back.

So basically, Swiftsteel is great for focus fire, and Ironclaw is great for wrecking backline units.

What makes this complicated is Swiftsteel's 50% Critical Strike for Damage bonus. What it means is you can effectively run a +Power or +CritMult weapon instead of a +Crit weapon, so an extra +51% Power or +67% CritMult for a maxed out 5* weapon (which, depending on what your scalars are for your abilites, and what your stats allocations are before that, could be a 50%+ damage increase, or even more, but more likely in the range of +25%ish).

What that means is that the actual "must be attacked this often" value for Ironclaw to match Swiftsteel is:
So the upper bound of Swiftsteel's extra damage output to equal the average damage output of Ironclaw means the Ironclaw unit needs to be attacked an average of 1.71 times a round, which for most tanks is easily hit, even if they aren't tanking (given the prevalence of AoEs). So even with the Swiftsteel +Crit, Ironclaw is actually still fairly powerful in the Tank niche. However, if you have a Counter already built in (ie: Valorborn, or given by Diana), or Rallies, you may be better off going Swiftsteel because you'll have more than one chance to proc Swiftsteel a round, and you'll quickly outstrip Ironclaw with that.

Swiftsteel vs. Wartech

Here's where comparisons get complicated. The two act so very differently, making direct comparisons don't quite work. I'll be making some assumptions/shortcuts to make them easier to compare as DPS increases, but what a Hero's A1 is, and what you're aiming for really dictate this decision.

For DPS purposes, though, Swiftsteel is effectively 40% of an A1 and 50% bonus crit, and Wartech is effectively +100% CritMult.

Let's make some other assumptions: 100% Crit regardless of set; frees up Swiftsteel for a +Power or +CritMult weapon. That assumption means that you'd be getting 2/3rds the CritMult that Wartech gives, which given the 40% extra A1 DPS on average means that if you're only using A1, you're going to do more damage with Swiftsteel. On average.

Average is a dangerous word here, however, because most arena fights are over in a couple rounds (or drag on forever). You might decide to go +Power for more consistent results instead, but similar to the calculations we did for Wartech alone, on both cases Wartech will still burst higher than Swiftsteel.

The other thing that makes "average" dangerous is that Wartech favours AoEs heavily. A single AoE can only proc Swiftsteel once, however, you get the potential benefit of Wartech for all the hits of your AoE.

So once again, if you want consistent output, Swiftsteel may be better, but Wartech will give you better burst capability. And of course, if your A1 is an attack you want going off a lot, Swiftsteel is probably the way to go. If most of your damage is AoE, you probably want to go Wartech still. Anything that gives you extra potential Swiftsteel procs will favour Swiftsteel as well (Counters, Rallies).

The Future of Swiftsteel

The Swiftsteel changes effectively made Wartech niche (where before Wartech was the "best" and Swiftsteel was niche). There's a rumour that Swiftsteel proc rate will be reduced, which will bring it more in line with Ironclaw and Wartech so it's not quite so overwhelmingly powerful, but honestly, it's not the proc rate so much as it's the +50% Crit bonus that allows it to be such a great DPS tool. +51% Power on your weapon is potentially a massive DPS bonus--+67% CritMult is potentially less of a huge bonus unless your Hero doesn't scale well with Power, or you've already got a lot of +Power as the two scale off each other.

As long as that Crit bonus exists, or exists at that level, Swiftsteel will likely be the go to 4P for DPS. To balance it, the Swiftsteel proc rate would have to be reduced to the point where you'd rarely see it proc, defeating the original purpose of the set. +30% Crit would've been more reasonable, as then you could have the question, do I Elderthorn for my 2P? Do I Weapon for +35% Crit? Do I do both? Can I get Jewel slots to make up the deficit of one or the other? Right now it's basically, Swiftsteel, 7 Crit Jewels, go. Or Swiftsteel, Elderthorn, 3 Crit Jewels, go. Swiftsteel makes it way too easy to hit the Crit cap.

With that in mind, I forsee a nerf to Swiftsteel's Crit bonus one day (after the designers try the proc reduction), or a buff to Ironclaw/Wartech, although in the right situation Ironclaw will crush Swiftsteel's output today so I'm not sure about buffing it too much. Similarly, Wartech's upper bound already hits so hard today that buffing it the wrong way could be dangerous to game balance--part of why just buffs only doesn't really work as a game balancing tool despite people constantly suggesting it. The math breaks down eventually. Sometimes you just have to nerf.
#Theorycraft, #AllianceHotS

Wednesday, July 5, 2017

[IndieDev] Checkpoint Saves: Ugh, Why? And How, Part 2

Last week I chatted about the start of Eon Altar's save system, why it didn't work, and how we fixed it. This week I'll go in-depth about Eon Altar's Checkpoint Save system. 

Fast forward nearly a year from our new save system implemention--Aug/Sept 2015--when we finally entered Early Access. The game was probably about 80% functionally complete and 60% content complete. As I like to say, the last 20% of your game will take about 80% of your time, and Eon Altar was no different. We spent 10 months in Early Access, and initially, the biggest point of feedback we got was, "How can I save my game mid-session?". Our sessions were about 30 minutes to 4 hours depending on the players, and in 2015 shipping an RPG without the ability to save mid-session was, well, pretty bad. So began the process to create a checkpoint save system, and retrofit our levels to save data correctly.

Checkpoint Saves: Less Complex?

Why checkpoint saves, though? Why not save anywhere the player wanted? The answer to that is largely to reduce potential complexity. If a player can save anywhere and anytime they want, it means you have effectively an infinite number of states, and good luck testing that. A specific example of this would be Myrth's Court in Episode 1: The Prelude.

Myrth's Court
That "moment" as a whole had the following:
  • A check to see which player characters were available.
  • A dialogue based on that to posit a vote.
  • A vote to decide which character's solution to use.
  • The actual moment where the party implements aforementioned solution.
  • Potentially a combat as a result of the solution.
If players could save at any point in that process, that would significantly increase the testing complexity around that moment. What happens if you reload with different characters mid-moment? What happens if you have fewer characters? More characters? By only allowing saves to occur at specific points in the level, we can avoid having to test those mid-moment saves.

By using checkpoint saves, we could tie them to an existing checkpoint mechanic we had in the game already--Destiny Markers/Stones. Again, not having to worry about partial encounters is a huge complexity save, but also not worrying about how to turn on/off saving in certain locations. What if we had a bug that prevented save from being turned back on? Or a bug that allowed saving in the midst of a complex moment? Also, how do we communicate if we can save or not to players? And what would the save UI look like? By tying it to an existing checkpoint mechanic, it made it very easy to communicate and very easy for players to grok. No special rules or explanations necessary.

So while checkpoint saves aren't as convenient for players, the reduced complexity was enough to make checkpoint saves doable with our small team and budget.

How to Train Your Save System

We already had a method to quickly save data to disk, and the checkpoint save system would continue using it. The questions then became, where do we store that information at runtime so designers could access it, and how do we design it in such a way that required as little designer input/time as possible?

First we had to determine what we would have to save:
  1. Enemy spawner state: were they dead or alive?
  2. Game object state: was it enabled or disabled?
  3. "Usable" object state: was it waiting or already used?
  4. Finite State Machine (FSM) state: what state was it left in?
  5. Specialized game object state: what is the game object's transform (position, rotation)?
  6. Specialized spawner state: what is the enemy's transform (position, rotation)? What is the enemy's AI settings (aggressive, passive, patrolling; allied to players, or enemies; patrol state)
With those 6 items, we could literally save anything and everything in our levels.

I created specialized game components that could track those states and report them to the save subsystem as they changed, so we wouldn't have to trawl through level data to extract information--remember, we wanted to ensure the save system was fast. All the designers had to do was add them to an object they wanted to save that particular state out for, and give it a unique ID (well, my code autopopulated the ID based on a random GUID and the name of the object in the hierarchy, but the designers could override that if they chose).

This worked extremely well. Design quickly retrofitted our existing levels. The vast majority of our save data is items 1, 2, and 3. FSM save data is rarely used unless the FSM is long-lived (our Destiny Markers are the primary users of this tech). Most FSMs would trigger and finish in one go, or at least in one encounter so we'd not have to worry about partial FSM execution by the time we got to hit a save point (yay checkpoint saves!). 5 was almost never used outside of redirecting patrol nodes for NPCs, and 6 was generally only used on super special NPCs: ones that changed their AI based on designer scripts, or NPCs that were used for escort quests.

Wild Checkpoint Data draws near!
The code took about a week to create/test/deploy for design. The lion's share of the time (and bugs) was designers retrofitting levels. I think it was easily a full man-month of time to get the levels up to snuff, and the amount of testing required was still absolutely immense, despite the reduced complexity of checkpoints.

The Bugs
A pitfall of this--and I'm not sure there's an easy way to solve this pitfall, I don't believe it's specific to this solution--is when designers forgot to put save components in levels, or they chained components in such a way that would create a problem on game load.

A specific example of this is a door in Episode 2, Session 1. Level design logic had the door with the following states: unopenable, locked, unlocked, open. Depending on the quests you did in the level, it could become locked, unlocked, or open. However, if you saved and quit and reloaded later, then the door would be unopenable because the door wasn't actually saving its state out, and players would become blocked.

Now, when we ran into those issues, we would add the save component in the level data, and then use code that ran on save data load to modify the data before it got applied to the level itself. Basically, we could determine based on what other quests were complete and save object states if the door should be locked, unlocked, or open, and set that state in the upgrade code.

Today we have 10 such save file upgrades that potentially run on a save file to give you an idea of how often we've had to use this, and the lion's share of them are for Episode 2 Session 1. Enough to make me glad we implemented it, but just how different E2S1 was from the rest of the levels really showed how easy it is to screw up save state if you're not careful thinking about it holistically.

The Future: SPARK: Resistance

SPARK won't have need of checkpoint saves, as sessions won't last more than 10-15 minutes at a maximum. Rather, any save data will be related to your "character". Unlocks, experience, statistics, etc. Thankfully, I'll be able to take our save system nearly wholesale from Eon Altar and apply it here, minus the checkpoint stuff.

A Randomly Generated Map and Associated Data
The in-level checkpoint stuff wouldn't work in SPARK anyhow, as the level structures are fairly different to start with thanks to both the procedural nature of the levels as opposed to hand-crafted, and the fact that the levels are networked right from the start, which is very different from a local multiplayer game.


The current save system in Eon Altar is robust, extremely fast, legible, easy to modify, and minimalistic in data requirements aside from the fact that it is XML, but the actual data output is all essential. It requires as little designer input as I could possibly get away with (even most checkpoint save data is attached to prefabs and autopopulates all IDs in the scene at the click of a single button). 

Yes, it took a fair amount of engineering work altogether, but I think that's a result of you just cannot skimp on engineering for a system like this. You get what you pay for, and if you're not willing to put the engineering time in, you're not going to get a great system on the other end. And as mentioned at the beginning, persistence is extremely important to games. A game can't afford to skimp on their persistence systems in my personal opinion.
#IndieDev, #EonAltar