The Case Of Dancer Auto ATTACKS

Hello, Fürst here and I wanted to tell you about a little story that happened on our Discord server a while ago about solving a simple question:

How do you calculate the damage of a Dancer’s auto-attack?

The Original Question

On our Discord server we have a public available questions channel, where we usually help people out with their questions to the formulas that we have established since HW. That day we got a seemingly very straightforward question from a user named “Lepaw van effectrix”:

Lepaw van effectrix asking why DNC autos were not matching up with 100 potency
Lepaw van effectrix asking why DNC autos were not matching up with 100 potency

When we create the formulas we usually try to test a lot of edge cases. Sadly even to this day we couldn’t establish a general damage formula that is always spot on. But a 10% error over 5000 hits is a bit more than just being 1-2 points of damage off from the real value. To rule any form of user error out we decided to ask a few questions. Very often when something “is off”, the used formula is just missing a parameter or two which then leads to a false result.

Emiin reminding to account for weapon delay adjustments to auto potency
Emiin reminding to account for weapon delay adjustments to auto potency

But this was apparently not the case:

Lepaw showing his work adjusting for weapon delay
Lepaw showing his work adjusting for weapon delay

Lepaw proceeded to break down what exactly he did and how he applied our formula in his test. It looked like he did everything correctly on that side of things.

So is our formula just wrong? Does SE treat Dancer maybe differently than other physical rangeds? Or did Lepaw make a mistake after all?

In order to answer that I wanted to reproduce the issue with a similar but easier test.

The Formula

I thought it might be interesting to show you the general testing process we employ at AS. First of all we need the damage formula:

Auto-Attacks

  • D1 = ⌊ Potency × f(ATK) × f(DET) ⌋ /100 ⌋ /1000 ⌋
  • D2 = ⌊ D1 × f(TNC) ⌋ /1000 ⌋ × f(SPD) ⌋ /1000 ⌋× f(AUTO) ⌋ /100 ⌋ × Trait ⌋ /100 ⌋
  • D3 = ⌊ D2 × CRIT? ⌋ /1000 ⌋ × DH? ⌋ /100 ⌋
  • D = ⌊ D3 × rand[95,105] ⌋ /100 ⌋ × buff_1 ⌋ × buff_2 ⌋

This behemoth of a formula looks quite intimidating at a glance. But all it really does is multiplying a whole bunch of factors together and flooring in between.

Lepaw used his full gear, so he needed to use the full formula. But since we are only interested in the potency of our auto-attack, we can use an easier scenario: Remove every piece of gear that contains SkS and Det and focus only on the hits that do not crit or DH. Like this our formula simplifies to the following:

Auto-Attacks

  • D1 = ⌊ Potency × f(ATK) ⌋ /100 ⌋
  • D2 = ⌊ D1× f(AUTO) ⌋ /100 ⌋
  • D = ⌊ D2 × rand[95,105] ⌋ /100 ⌋

Since SkS, Tnc and Det are at base value, f(SPD), f(TNC) and f(DET) just return 1000 which cancels out with a /1000 to 1. Dancer’s trait doesn’t affect auto-attacks so it also cancels out to 1. So we just multiply a bunch of 1s to the factors that do influence our damage and can as such ignore them. Since we also do not look at direct hits, or critical hits, we can further ignore the CRIT and DH part as well, since it once again just factors to 1. The thing we are left with is now quite easily workable since it only depends on WD, AP and Delay.

You might be thinking “But hey, there is still one part of the formula left, the random damage roll!” and that is sadly correct. Since we have no way of knowing what multiplier between 95-105% got applied to a damage when we hit something, we are interested in the damage hit at 100%. Let’s call this the “mean damage”. When you hit something ingame for let’s say 100 mean damage, you can do any integer between 95 and 105 damage. But how do we even know a hit in game was the mean damage? After all we do not know which multiplier is applied for the damage roll. For this we need to find the highest and the lowest roll for a given set of substats. Let’s say X is the mean damage we want to figure out. The equation:

  • Highest hit = ⌊ 1.05 × X ⌋
  • Lowest hit = ⌊ 0.95 × X ⌋

This has only one solution.

This might sound not so bad at 100 damage since that’s only 10 different values, but imagine with our current gear how much damage our actions hit for. A mean damage of 10,000 results in 1000 different possible values. Since it is random which damage value you hit for, hitting the highest hit of 10500 has a 1/1000 or 0.1% chance, which makes the actual testing process (hitting a dummy) a possibly very time consuming activity. With the mean damage we can further simplify the used formula to:

Auto-Attacks

  • D1 = ⌊ Potency × f(ATK) ⌋ /100 ⌋
  • D = ⌊ D1× f(AUTO) ⌋ /100 ⌋

or:

Auto-Attacks

D = ⌊ Potency × f(ATK) ⌋ /100 ⌋ × f(AUTO) ⌋ /100 ⌋

The Test

Now to actually test it ingame, I picked some gear that works and went on to hit a striking dummy. My stats were the following:

Fürst's DNC Character Stats
Fürst's DNC Character Stats

I used the AF4 weapon since it was the only easily available weapon that doesn’t have any Det or SkS on it.

DNC AF4 Weapon: Weathered Krishna
DNC AF4 Weapon: Weathered Krishna

It really didn’t matter what was used, as long as I was at base Det and SkS after all. Then I kept hitting the dummy. Luckily auto-attacks happen automatically, so I just had to leave my character to hit the dummy for a while and come back later. We use ACT for the actual data parsing process and after a while I saw that I actually got the highest and lowest hit:

Highest normal damage roll auto recorded
Highest normal damage roll auto recorded

and

Lowest normal damage roll auto attack recorded
Lowest normal damage roll auto attack recorded

These result in a mean damage of 993. We can easily double check if this is actually the full range by checking that ⌊ 993 x 1.05 ⌋ = 1042 and ⌊ 993 x 0.95 ⌋ = 943.

But what do we expect as the theoretical value? Let’s plug everything into the formula:

  • D = ⌊ Potency × f(ATK) ⌋ /100 ⌋ × f(AUTO) ⌋ /100 ⌋
  • D = ⌊100 × (100+⌊165 × (1308 - 340) / 340⌋) /100 ⌋ × (114 + 39) × (3.12/3) ⌋ /100 ⌋
  • D = ⌊100 × 5.69 ⌋ × 153 × 1.04 ⌋ /100 ⌋
  • D = ⌊ 569 × 159 / 100 ⌋
  • D = 904

So we are indeed roughly 10% off. Applying 110 potency for Dancer autos nets you exactly what you were searching for: D = 993

The Result

Dancers use 110 potency autos, just like Casters, Healers and Melee Jobs

Case closed, right? But was it really the potency? Why would one Physical Ranged randomly do more auto damage than the other ones? They all do exactly the same right? Do they maybe just get a hidden trait? Since instead of using 110 potency they might just get a 10% buff.

The next question I was asking myself was “Is there maybe something wrong with the action itself?”. I noticed something that was there in the data, clear as day, for everyone to see, but nobody, no Theorycrafter etc. seemed to have noticed it before.

Let me show you:

When a Physical Ranged does an auto, they use an action called “Shot” internally, which has 100 potency. This is parsed obviously:

Physical Ranged jobs showing 'Shot' autos in ACT
Physical Ranged jobs showing 'Shot' autos in ACT

If you do the same with a melee job, or a caster it will use “Attack” instead, which has 110 potency and looks like this in ACT:

Casters, Healers and Melees showing 'Attack' autos in ACT
Casters, Healers and Melees showing 'Attack' autos in ACT

Now if you look at the picture of the data gathering process above you will see it already:

DNC's Auto listed as 'Attack' from the previously gathered data in ACT
DNC's Auto listed as 'Attack' from the previously gathered data in ACT

So no Theorycrafter ever noticed that Dancers use literally the melee auto-attack action. It was there all along, but it took the closer look of Lepaw to finally show it to everyone.

Now you might wonder “But why?”. I do not know. It might be intentional. It might be a reminiscent of a time where Dancer was a melee in the internal build and SE just forgot to change it. All we know is that, it’s there, and if there is anything to learn from this little story, then “Take nothing in this game for granted and challenge established formulas”.

~ Fürst