ChthonVII / d2r_24ptr2_wereform_calculator

Wereform IAS Calculator for Diablo 2 Resurrected 2.4 PTR2
GNU General Public License v3.0
4 stars 0 forks source link

Fury is wrong #1

Closed Warren1001 closed 2 years ago

Warren1001 commented 2 years ago

Hi, I'm Warren from Llama's community. I've been working on my own IAS calculator for some time now and have put in considerable effort for 2.4 Fury to no avail.

The breakpoints you calculate for Fury are wrong, but I don't know what the solution is. I've spent weeks trying to figure it out but I haven't figured it out.

In my efforts to figure it out, another player and I recorded EIAS values from in-game by skipping frame by frame in videos and counting frame lengths of animations. These tests were done by holding down the attack button and letting 3 attacks play before letting go, ignoring the first opening hit and counting the repeating hits as well as the final swing, ignoring the single frame at the end of the animation, and count the opening hit on the second swing, then verify proper counts by counting all the way through the remainder of the swings without assumptions. The EIAS values were determined by setting Werewolf to give 5*(slvl-1) SIAS and increasing Werewolf by 1 level then refreshing the transformation.

Here are the results, which are mostly conclusive: https://1drv.ms/x/s!Atswm4Q6kxQElC3RoxqLl_tLBZfT?e=xV6QSJ Ignore the Detailed pages and any numbers with the label 'Calculated', those are my attempts at trying to figure out the formula. The Detailed pages contain some exact breakpoints.

Things I've noticed: despite the repeating swing's framePerDirection constant being different for Druid for 1HS and 1HT, they have the exact same breakpoints (again, no assumptions or skipping were made during testing). Additionally, I have not once noticed or recorded a Fury in which the repeating hits' lengths varied. So far they have all been the same.

I am at a loss for what the issue is, I have fiddled with all the variables, and I have strong confidence that there is either a new (or deleted) element at work or it's a multi-variable difference compared to pre-2.4.

I suspect that the repeating swing's framePerDirection constant uses the final (standard) swing's framePerDirection constant somehow, since the only thing 1HS and 1HT share is an equal standard framePerDirection constant. But no amount of fiddling with integers for the repeating swing has given conclusive results.

I'm at the point where I'm considering hardcoding the Fury table instead of calculating it.

I have not tested any other Wereform skill (besides minimal testing with normal attacks in Werewolf being as expected in 2.4), so I cannot say if anything else is wrong or not.

ChthonVII commented 2 years ago

I'd like to know more about your methodology.

Some questions about the spreadsheet:

  1. The first column is the level of your modified werewolf skill, correct?
  2. And the second column, is just the skill_IAS from that skill, correct? Neither WSM nor gear_IAS is factored in, correct? (And I'm going to assume you have no gear IAS.)
  3. Why do you have a column for 1 frame between Furies? So far as I am aware: (1) By convention, FPA has always been consider to run from the start of one skill activation to the start of the next. Not counting that frame puts you a off-by-one relative to from every FPA discussion there's ever been. (2) The first and last frames of the attack animation sequence are neutral frames (although you might not see either with a fast EIAS). (3) When the animation sequence aborts, the next animation is commenced on the same frame.

Some questions about methodology:

  1. You are using the legacy graphics mode, right?
  2. Do you have any solid information on how D2R deals with rendering the legacy graphics at more than 25 FPS? I know legacy D2 just duplicated frames (well, engine frames, not except for UI stuff) and lived with the resulting stutter, but I have no idea how D2R does it. If there's any kind of interpolation or temporal smoothing going on, that would pose a huge problem.
  3. Are you 100% sure your hardware is capable of running D2R while recording/encoding without dropping frames? (If v-sync is on, dropping even 1 frame at 60 fps might be enough to entirely omit a legacy-engine frame.)
  4. What video codec and container format are you using?
  5. What are you using to play back the video frame by frame? It may not be a safe assumption that this software is capable of frame-accurate seeking with all video formats. I'm aware that the video processing community spent years working on input filters that could guarantee frame-accurate seeking. (Suggestion: Try vapoursynth + ffms or lsmashsource + vsedit.)
  6. Suggestion: Use vapoursynth to do a "subtract the previous frame from the current frame" operation on your video. That should make it super obvious where the animation frame changes, and be much faster than reviewing every frame manually, and eliminate a lot of room where human error might creep in.
  7. Have you tried to compare your methodology's results against Onderduiker's "mana cost method"? This might reveal issues with your methodology, or the mana cost method might turn out to be faster and/or less painful than staring at tons of video. (In case you're unfamiliar: Use modded charms to adjust your max mana until you find the smallest max mana such that you can use the skill continuously without losing mana. Assuming no faster-regen bonuses, mana regen is floor((256 * max_mana)/3000) in units of 1/256 mana per frame. That tells you the rate at which your skill is using mana. Divide by the cost from skills.txt and you've got activations/time. Flip that over to get time/activation. FYI: Because you can't be short by a smaller amount than 1/256 mana per frame, the smallest net loss you can get is just under 1 mana per 10 seconds; which gives you an idea how long to run your tests.)

I think you might find it worthwhile to read lines 3149 through 3333 of my code. The approach is probably not what you're expecting, and there are a lot of useful sources cited in the comments that might prove helpful to figuring out what's going on.

As soon as I can find the time, I'll try to verify some of your results against the mana cost method and then start futzing with potential changes Blizzard might have made that aren't totally insane to see if anything fits.

Warren1001 commented 2 years ago

Spreadsheet:

  1. Yes, that's the modified Werewolf for self reference, I hadn't planned on showing the information to public so it's not very nice to look at lol
  2. Correct, forgot to mention that. EIAS listed in the tables do not count WSM, and no other sources of EIAS were part of the test.
  3. The reason I'm ignoring a single frame between subsequent animations is because all currently common calculators hack off 1 at the end of the FPA calculation (including your Frenzy calculator). To accurately compare, I'm doing the same. In case this was somehow an error, I did record my ignoring of such, but that would only increase the final swing's hit by one frame, which doesn't change much. Everything else is still wrong either which way.

Methodology:

  1. Yes, legacy graphics of course.
  2. No, I do not. Are you sure Legacy Graphics is in 60 FPS? I believe they took the smallest amount of approach to updating Legacy Graphics, as its in the basic Direct3D video mode.
  3. Am I confident? No. OBS is a bitch and drops every other frame for some reason on some recordings. But I am able to see these skipped frames as I skip through them, and if I notice this happened, I re-record the video. It's been an all-or-nothing type of ordeal so far.
  4. x264 with flv. FLV was the only container which allowed me to seek backwards through frames.
  5. I am using SMPlayer. VLC is bugged with frame by frame playback so I browsed around to find something else and this seemed good enough.
  6. Interesting, I'll keep note of that. I had searched for better ways to do this but didn't find anything fruitful.
  7. In terms of just determining my total frame length recordings are indeed correct? I could.. but I'm pretty confident my recordings are correct. That only helps with total frame length so it doesn't help me obtain the goal I'm aiming for.

I'm not well informed enough about videos and rendering and the graphical engine of D2/R to say I am 100% correct, but if I had skipped frames or an issue with D2R's Legacy Graphics then I would have been able to tell from the video. I looked at every frame of every swing of every animation and saw the same results counting them 3 times each. It was always the same, even the image each frame used for every swing, nothing varied. Every keypress to advance the frame did indeed advance the frame. If there were skipped frames, my advancing through the frame would appear to do nothing for one frame, which I would then discard the video and try again.

The only reason I'm willing to doubt everything I have now is because most of the breakpoints are so frustratingly close in some cases that I don't understand what I'm missing.

ChthonVII commented 2 years ago

I've looked a this a bit and found:

  1. There was a bug that was causing the action frames to sometimes trigger one tick too soon, especially at slow EIAS. That's fixed now. Unfortunately, it doesn't really move things closer to your data.
  2. After thinking about it more and rereading Myrdinn's debug analysis yet again, I've come to the conclusion that you're right and everyone else in the history of D2 is wrong about that extra frame. The debug goes through 18 ticks, numbered "tick0" to "tick17." On tick0, the animation's frame000 (which is a neutral frame) is displayed, and the animation counter hasn't started yet. On tick17, the animation is aborted and the next animation is loaded, making tick17 also tick0 for that animation. So 17 ticks go by per animation, and yet everyone calls this "16 FPA." Because everyone is ignoring that neutral frame on tick0. So... I'm not really sure what to do with that. Count tick0 to be accurate? Or don't count it to be consistent with what everyone means when they say "x FPA"? Or makea toggle switch and document it as clearly as I can?
  3. (Related to #2, I'm pretty sure you have swing 1 for the cryptic axe wrong because frame000 gets played twice, on tick0 and tick1, and I bet you weren't watching for two neutral frames back to back. This is going to happen any time the final EIAS is less than 100.)
  4. As you point out, the calculator's output sometimes has a "worble" in the middle 3 hits -- something like 3/4/3 or 4/3/4 -- while your data has no worble. This leads somewhere. We know for certain that the rollback is now 70, because we can extract skills.txt and it says so. The code on lines 3318 and 3319 is a direct port from the machine code of legacy D2. (See source #5 in the comments to that function.) The rounding on line 3318 is what causes the worble when rollback is less than 100. So, we're faced with two possibilities: Either your data is wrong because it doesn't worble, or Blizzard changed the code this line is based on to remove the worble. There is only one way to fully remove the worble: replace currentframe in line 3318 with actionframes[whatever]. (When I fixed the bug mentioned above, I also stored actionframes[whatever] in actionframeflag to make that value accessible later.) The output from this potential change doesn't line up with your data, but it's closer. There are also two possible ways to mostly remove the worble: (1) multiply counter, rather than currentframe, by 0.3, or (2) multiply counter, rather than currentframe, by 0.3, and then continue back to the start of that frame (the next lowest multiple of 256). The former makes things drastically faster and so is very probably wrong. The latter is also sorta-kinda close to your data, but with some worbles. So, I'm going to guess that either Blizz did the full worble-removal thing and your data is a little wrong, or Blizz did nothing and your data is totally wrong. I'm somewhat reluctant to change line 3318 without absolutely ironclad test results, because (a) it's definitely correct via a vis legacy D2, (b) I'd don't see what purpose would be served by Blizz changing this, and (c) the change would also upset the breakpoints for Strafe and Fend.
Warren1001 commented 2 years ago
  1. Interesting, did not know that.
  2. When I first got into the IAS calc making business, I wondered why we were subtracting 1 at the end for no reason when I can clearly see the animation takes that long naturally. But no sense in changing semantics now.
  3. It's possible, I would like to clarify that by avoiding frame skipping, I meant the entire window rather than just the werewolf animation. I understand that in some cases you can have two identical frames back to back, and I believe I accounted for them correctly. But yet again, I'm more than willing to be proved wrong if it means figuring out what Fury actually is.
  4. If there's one thing I'm confident about, its that all tested EIAS values had no "worbles", and I never noticed any "worbly" animations in any of my non-recording tests either. I recorded at 25 FPS and I could count the entire animation sequence out for 3 subsequent attacks and not miss a beat, and not once did one of the repeating hits differ.

Some other generalizations I'd like to make. I would find it likely that the rollback calculation is incorrect. I can forcefully set frames to the actual in-game frames at any place on the Detailed pages so that only rollback is being calculated, but in no way does the rest of the subsequent calculations fall into place. To explain in more detail, I can set the frame length in the calculations for the first four hits and let only rollback be the contributing factor and the last frame will still be wrong. You mention the possiblity of the first hit of Cryptic Axe being wrong when EIAS < 100 due to duplicate neutral frames, however even >100 EIAS values are off the mark completely. The ONLY variables that deal with calculating the first frame are total EIAS, animation speed, a couple of factors/coefficients, the framesPerDirection constant used for the first four swings, and the startingFrame, when considering original formulas. I have changed all of these in various ways and never found anything solid. The fact that the rollbacks are likely calculated wrong AND the first frame is calculated wrong, the issue may lie with any shared variables between these two calculations.

Edit: If you believe the worble removal would impact other worble skills like Strafe, sometime this week I can load up a modded session with an Amazon with 100% CTC on Attack with something like Bone Spear or just some fire damage and test against the currently known breakpoints for Strafe and see what happens.

ChthonVII commented 2 years ago

Oy. I just thought of another, really fundamental, problem with video analysis. Unless Blizz hired someone to draw some more, there only exist 13 animation frames for werewolf's A1 animation (and 2 of those are neutral frames). So when we now use the borrowed value of 17 or 19 or 21 or whatever framesperdirection, some of those 13 frames are going to have be doubled up in the legacy engine to make that work. For (totally made-up) example, frame006 of werewolf's A1 animation might be used for both frame009 and frame 010 when using a borrowed value of 19 framesperdirection. And then the renderer is doubling up the frames from the the legacy engine -- some twice and some thrice -- to get to 60 FPS. So, when you see the same werewolf animation frame in X consecutive 60FPS video frames, you have a nasty decoding problem trying to distinguish werewolf frames duplicated because the animation logic called for playing the same frame twice, versus werewolf frames duplicated because multiple logical frames are now mapped to the same drawing, versus legacy frames doubled-up by the renderer, versus legacy frames tripled-up by the renderer. I'm doubtful it will be possible to correctly decode this problem for all values of X. And then you have the issue of potential dropped frames on top of that. Dropping a single frame at 60 FPS leaves you not knowing what X is in the first place. Realizing this makes me doubt whether video analysis of wereforms is even possible under this new regime of borrowed framesperdirection values.

(Yes, the legacy graphics are 60FPS. Unless you have an expensive monitor, it's physically incapable of anything else. Legacy D2 works like this: In single-player mode, it outputs 25 FPS and punts the problem of getting to 60 FPS over to the graphics library. In multiplayer mode, it doubles-up or triples-up frames from the engine to get to 60 FPS, and then draws the UI on top at 60 FPS. (FYI: There's a PlugY-loadable thingy that enables the multiplayer rendering method in single-player.) I strongly suspect that D2R legacy rendering works D2 legacy multiplayer rendering. The mouse pointer is so painfully jerky at 25 FPS that you'd definitely notice.)

[Edit: I just saw your reply. I'm really sorry to tell you this, but if you recorded at 25 FPS, I'm afraid your data is all junk. Because there's no guarantee your recording software's 12->5 sampling pattern is going to align with the renderer's 5->12 frame duplication pattern, you may have ended up entirely dropping some of the 25 frames from the legacy engine. (E.g., if the duplication pattern is 1, 1, 1, 2, 2, 3, 3,... and the sampling pattern is 1, x, 2, x, x, 3, x,..., then you got two copies of frame 1 and lost frame 2.) To have any hope of getting this to work, you'd have to start with all 60 frames and then work out a set of decimation rules that would get you back to the correct 25 -- which may turn out to be an insoluble problem due to the double-layered doubling happening in some places. In fact, given that problem, I'd strongly recommend switching to the "mana cost method."]

Warren1001 commented 2 years ago

I'm all for other means that don't involve video recording, but I don't know of another way to accurately count each hit rather than total frame length like the mana method. I suppose I can use the mana method to confirm whether my breakpoints are correct or not, and then move on from there, but I'm pretty busy this week.

I'm still willing to believe theres no worble. In the event my frame counts are off, I'd still believe there's no worble (or very, very, very little of it) as watching the animations in-game as I attack truly feels like there is no worble.

Edit: if you're interested, I can send you some of the recordings I have. I recorded in extremely low bitrate so the file sizes are ~10 MB. I have a video for each weapon I used in my testings except Repeating Crossbow (I hadn't set to low bitrate yet so big file size). They're 1-2 minutes long and just involve me hitting constantly.

ChthonVII commented 2 years ago

Yes, I think it would be best to first nail down the overall cycle breakpoints with absolute certainty. That may be enough to get us the individual hit frames too, since it's unlikely more than one plausible formula will match all the cycle breakpoints. Even if it's not, it should be a lot easier to massage the formula when we know for sure what it has to add up to.

Coincidentally, it appears that fixing that bug eliminated most of the worbly breakpoints for everything except one-handed swinging. (Pure luck. It just made the rounding break in favor of not-worbly in more cases.)

I'm afraid I can't do anything useful with 25FPS videos. I can offer some suggestions for how to maaaaaaaaaaaaaybe get usable video in the future though: First start by finding a background object that constantly animates, like a fire or something. Better yet, find a spot with several. This should hopefully make it possible for an automated decimator to pick out the right 25 frames from 60. Next, record 60 FPS, x264, probably a mkv container. Feed that into vapoursynth using ffms2. (You can use vsedit to view the output from your vapoursynth script frame by frame (forward, backward, arbitrary seek), and ffms2 should be frame-accurate.) Now, build your script. Start with a decimator -- probably TIVTC's TDecimate() function in mode 1. Hopefully that will latch onto the changes, or lack thereof, in the background object and correctly select the right 25 frames out of 60. Next, crop around the druid since you no longer need the background. Finally, define a second clip using "subtract the previous frame from the current frame" and stack it side-by-side with the original clip. This should clearly highlight animation frame changes. You can pipe your output to an encoder to save it, but you don't need to since you can just view the output in vsedit. As for analysis: When final EIAS is below 100, you won't be able to tell the same frame playing twice from two logical frames that map to the same picture due to the borrowed framesperdirection nonsense. When final EIAS >= 100, it should always be the latter case. All that said, I'm still not sure it would produce usable video, so I'd still start with mana cost testing.

If I get around to making IAS and mana charms before you do, I'll send you a save file full of them.

Warren1001 commented 2 years ago

Question, when using the mana cost method and continuously casting, should the calculation result in the first 5 hits + the additional frame, or would that additional frame not be included? As in, how long is an animation truly when you're continuously casting?

Also, while I'm asking questions, should you be aiming for a mana regeneration during continuous cast that outsustains the casting or should you be aiming for the mana regeneration breakpoint just under that? Would an issue arise from being slightly over on the mana regeneration breakpoint?

I went ahead and did some tests. I'm fairly confident I understood it correctly? You can review the data and the math to be sure it's correct. The actual tests were done on the fourth sheet (only one with a name). The first three were mathematical tests for frame->mana backwards logic to try to make sure I understood the math correctly. Here's the data: https://docs.google.com/spreadsheets/d/1cw-Hn5ZmA6xQGvTMvwB6VIJACbn4IQ4oRqObRPWzqeA/edit?usp=sharing

The testings were with the modded Werewolf as before with a Caduceus. I used mana charms to put myself up to the mana values listed there.

It looks like my frame video recordings were exact for Caduceus. Do note I only tested against total frame lengths less then 38, as frame lengths 38 and higher cannot be counted with a mana cost of 4 (see sheet 3). If I mod the mana cost to be higher I can test the slower breakpoints too, but I didn't bother.

ChthonVII commented 2 years ago

Off-topic: The $%&#@ b-net launcher just uninstalled my PTR and I don't have a backup. So no more testing for me until 2.4 launches :(

I believe you need to count the extra neutral frame that the D2 community traditionally doesn't count.

I believe you're suppose to aim for just above, then move a +1mana charm into your cube to test just below. (Or vice versa.)

I'll try to do an example:

First, let's define a word and say a "u-mana" is 1/256 of 1 mana. This will save me from having to type that over and over. For the example, let's pretend you want to test an EIAS setup that you think is 19FPA (which is really 20FPA with the additional neutral frame). Fury's mana is 4 and manashift is 8 (from skills.txt), so it costs 1024 u-mana. Since we think we have a 20 frame cycle, we need ceiling(1024/20) = 52 u-mana/frame to break even. Unmodified mana regen is floor((256 max_mana)/3000) u-mana/frame. So flip that around and we have max_mana = floor((rate 3000)/256). So we should need 610 max mana to break even. Now do the first test with 610 max mana. Your mana should dip to 606 when you use Fury, then regenerate back up to 610 during Fury's animation. Unfortunately, the mana display doesn't update every frame, and may have rounding issues. But I believe we could safely say that a mana value <= 604 means you're losing mana. You need to let it run for long enough to make sure you're not losing mana very, very slowly. The smallest amount of mana you can lose is 1 u-mana per frame, which works out to 1 mana in just under 10 seconds. So running for a minute or so should result in -6 mana. If you lost mana, that means the frame cycle < 20. (Our prediction was wrong. This EIAS setup is faster than 20FPA.) If your mana stayed steady, that means the frame cycle >= 20. (Our prediction may be right; do the next test.) Now move a +1 mana charm into your cube so you have 609 max mana and repeat the test. If you lost mana, that means the frame cycle is < 21. (Prediction validated!) If your mana stayed steady, that means the frame cycle >= 21. (Our prediction was wrong. This EIAS setup is slower than 20 FPA.)

Couple random notes: You need an invincible, immobile monster to hit so that Fury will go for 5 hits. I believe Onderduiker would mod the wooden towers in Act 5 to be immune to everything. Druids have even numbered mana per level and mana per energy, so you don't have to worry about having fractional max mana that the UI isn't showing you. (This can be an issue for other classes.)


Off-topic: Do you have any insights into what they did to dual wield? I was so focused on druids that I had very little time to test it. My very unscientific observations were:

Warren1001 commented 2 years ago

rip @ accidental ptr uninstall. pez said goal for ladder is end of month and 2.4 is suppose to be out before that, so maybe a ~week 2.4 comes out.

as for the dual wield stuff, are you sure they changed anything? as far as i was aware, they only fixed wsm bugging. im a bit lazy to check all the 2.4 notes for any additional wsm changes.

as for the mana stuff, i'll go back and revisit it another time, looks like i was missing important info

ChthonVII commented 2 years ago

Yeah, I clicked the dropdown to change to PTR, then clicked "update," and then the button text changed to "uninstall" while the button was depressed. I am annoyed.

The patch notes say, "Fixed an issue where your attack speed while dual-wielding could be incorrectly calculated depending on which hand it was held in or in which order they were equipped in." Based on the italic part, I had a suspicion this really means "we fixed the WSM bug, and also messed up the traditional dual wield calculation while we were at it." So I went and stared at Hammerman's Frenzy debug analysis until I figured out the root of the WSM bug. (Just in time for that info to become useless!) What I think we have is current_WSM = active_weapon_WSM - TruncateToInt((gloves_side_weapon_WSM + boots_side_weapon_WSM)/2) + boots_side_weapon_WSM. The bug snuck in because the first term appears in one step of the EIAS computation, while the latter two terms appear in a different step. The original programmer apparently didn't realize that the boots-side weapon becomes the active weapon sometimes during two-swing attacks (giving rise to the quirks of "unbugged" dual wielding) or that boots-side weapon becomes the primary weapon (and thus the active weapon most of the time) in the "WSM bug" state. Based on this, I was expecting that they probably changed it to current_WSM = active_weapon_WSM - TruncateToInt((gloves_side_weapon_WSM + boots_side_weapon_WSM)/2) + **inactive_weapon_WSM**. (This reduces to the average WSM unless one weapon's WSM is odd and the other even.) So I tried to sanity check that prediction with some quick tests. And the results were NOT what I was expecting. So I needed a new theory to fit the observations.

For quick testing I picked a phase blade and battle hammer because that gives you a -50 bonus or a +50 penalty, depending on configuration, under the traditional calculation. That's such a huge difference you can just eyeball it. So I was expecting normal attack to just use average WSM, and thus be the same in either configuration. Nope. While both configurations were obviously less extreme than under the old calculation, it looked like having the phase blade on the gloves side was slightly faster. (Which is the opposite of the traditional behavior.) So I counted attacks while running a stopwatch to verify that, and indeed it was faster. For Frenzy, I was expecting IAS on the boots-side weapon to be ignored due the the bug where it doesn't switch weapons if the first swing doesn't hit. Nope. IAS on the boots-side-weapon sped up the whole thing. If that bug alone were fixed, I'd expect the IAS to matter more in the gloves-side weapon, since the base animation length for that swing is longer. Nope. Counted attacks against the stopwatch and it's the same either way.

ChthonVII commented 2 years ago

Thank you for the mana cost testing. The only issue I see is in the row for ww lvl 9 -- 32 * 32 = 1024, so I'd think that should sustain. But I'm entirely willing to believe Blizzard has an off-by-one bug here.

Thanks to your testing, I believe I've got it. We are NOT borrowing framesperdirection and actionframeflags from the human form data. Rather we're using the werewolf values and multiplying the speed increment by werewolf_framesperdirection / human_framesperdirection!! I did a quick-and-dirty hack to make the calculator do that, and it matched your caduceus data perfectly.

Open questions this raises:

Warren1001 commented 2 years ago

Can you be a little more specific with what you mean by using the werewolf values and multiplying the speed increment? I can run some of my own tests on my calculator as well, and compare with the rest of my data and see if theres any portions where its off by 1. And does this only apply to Fury? I only did minimal testing but I found that running the standard human calculation for wereforms were correct for Druid using normal attack. Could this be wrong and more testing should be done?

I can get to testing with the other characters maybe later today or potentially tomorrow.

ChthonVII commented 2 years ago

Perhaps this is a better explanation: Wereform paw attacks are NOT borrowing the frame data from the human form animations. Rather a slowing factor is being applied to the traditional wereform animations so that the overall duration comes out the same as the human animation. (Though some breakpoints may be 1 EIAS off due to rounding issues.)

So all of the werewolf paw attacks use the traditional actionframe = 7, framesperdirection =13, animationspeed = 256. And there werebear paw attacks use actionframe = 7, framesperdirection =12, animationspeed = 224. Use those, not the human form values. Now, after we get to the step where we compute increment = TruncateToInt((animationspeed * cappedeias)/100) we now have an extra step were we apply the slowing factor: increment = TruncateToInt(increment * wereformnerffactor). And what's wereformnerffactor? It's (wereformframesperdirection * humananimationspeed)/(humanframesperdirection * wereformanimationspeed).

For normal attack this works out the same as just using the human frame data, except for a very few cases where rounding issues may move a breakpoint by 1 EIAS. But Fury often comes out quite differently because (a) the action frame is in a different place, and (b) each of the 4 rollbacks involves 2 instances of rounding. Coincidentally, having the action frame at 7 totally eliminates "worble" because floor(7 0.3) == floor(8 0.3) == floor(9 * 0.3) and you can't get further past the action frame in one tick without more than +150 EIAS.

I was able to answer a couple of my own questions since last night:

Unanswered questions:

Warren1001 commented 2 years ago

One thing I've noticed after properly setting it up with my calculator: while the breakpoints for Demon Crossbow are all correct too, one of them is off by 1 in terms of the EIAS required. Where the calculation returns EIAS 56, it was recorded needing 57. However, since its the only one that's been off so far, it's entirely possibly it was human error and it should be 56.

I have spring break coming up next week, so I'll have a lot of time to test stuff. Until then, I have a bit too many assignments to ignore, so I probably won't get much testing done in the next two days.

ChthonVII commented 2 years ago

You mean the breakpoint for 5/4/4/4/7 (24)? I've got that at 117 (57 + 60). Maybe you've got a rounding/order of operations error; or maybe you're flooring a negative number; or maybe this might be an instance where the formulae don't properly capture what the animation routine is doing. (That's why I decided to make the calculator simulate the animation routine rather than rely on formulae.)

Good luck with your exams.

Warren1001 commented 2 years ago

interesting, i think i may recall stuff being off by 1 beforehand as well, ill have to do some testing.

not sure if u'd find this interesting, but in 2.4 ptr, and itll likely release to live unless they found it themselves and fix it, llama and i came across a bug from a casual player where the underground passage doesnt connect to the dark wood. its just a dead end cave. when trying to load the tile that contains the cave in the dark wood, you crash immediately. the map generated correctly in live d2r and in LOD, so something they did in 2.4 fucked things. very weird and interesting

ChthonVII commented 2 years ago

Hmpf. I can't even recall anything in the patch notes that seems like it would be relevant to that. All in all, while I do appreciate the Fury buff, I'm thinking 2.4 was a mistake. This team does not appear to be up to the task of mucking about in the original code without breaking things (or, at least, not on the deadlines they've got), and they seem to be introducing at least as many bugs/bad decisions as they are improvements.

Gel87kjetil commented 2 years ago

The - 1 in the end of none serial/sequence attacks are something used to smoothen out the animation. I have also heard that d2 is using a 24frames breakpoint system on a 25 frame engine(now scaled to 60). Dno if it will change any output though.

Did you guys find a correct formula? :)

Warren1001 commented 2 years ago

Wanted to update with minimal Feral Rage testing. My same setup as before, tested Seraph Rod against 0 additional EIAS. Whether the hit connects or not is definitely the same length, and I recorded hit lengths at 21 on multiple occasions. This is the length of normal attack. It appears Feral Rage no longer has its niche identity and is now simply a standard attack animation.

Edit: to be more specific, I'm basing it off of your Standard Attack frame calculation on your calculator. I'll go down a few breakpoints and see if I get something to be wrong.

Another edit: I mentioned before I was seeing EIAS values off by one, but I have a better lead as to why. Look at Standard Attack frames for any weapon for Druid and change the Morph back and forth from Human to Werewolf with your calculator. They're almost identical, but some EIAS values are different by 1. I believe the difference to be correct, but why is this? This must have to do with where the rounding is, but it seems my rounding is not in the proper location? Because for my calculator, there is no difference when I switch the morphs, despite my confidence that there actually is a difference in-game. I have mine setup the same way. I figured out why this was the case, its rounding issues with the "wereformnerf" factor. I'm still having 1 off issues with that Demon Crossbow Fury breakpoint though.

Additionally, I'm noticing something even more interesting. With those 1 EIAS differences, I've been testing against them to see which one Feral Rage fell under. My in-game recordings for Standard Attack/Feral Rage (so far they've been the same) with a Seraph Rod in Werewolf have been slightly off from both Standard Attack in Werewolf and in Human. I'm recording that the frame 20 EIAS value is at -9, not -8 (which coincides with Standard Attack in Human), but frame 18 EIAS value at 1, not 0 (which coincides with Standard Attack in Werewolf). I'm using your calculator for reference since mine has the 1 off issue (though mine showed the same results in this case). I'm going to do more rigorous testing in the next couple days and see if I can confirm my findings and gather more data.

ChthonVII commented 2 years ago

That's a bummer about Feral Rage. But at least it does allow me to sidestep one of the two biggest points where the available debug analyses differ about how the animation loop works.

The breakpoints sometimes vary by 1 EIAS between human and wereform due to rounding. In cases where the human form increment would divide evenly or nearly evenly into 256humanframesperdirection, it's likely the adjusted wereform increment will come up just short and require an extra frame. Example: Human 1HS at 0 EIAS is 256 into 4864 which is an even 19 (and then subtract 1, so 18). But werewolf is 175 into 3328 is just over 19, so round up to 20 (and then subtract 1, so 19). 175 19 is 3325, so we're 3 1/256s of a frame too slow.

There are also likely calculator errors because I just guessed about where to insert this multiplier and how to do the rounding based on what best fit your data. Perhaps if we can accumulate enough of these off-by-one cases, we might be able to figure this out better.

Warren1001 commented 2 years ago

I believe I found the accurate way to round the new slowing factor. I think it just multiplies into the accelerating factor (accelerationSpeed * EIAS / 100) without rounding, and you only do the final rounding at the end (so one truncation). This change impacted my previously mentioned Seraph Rod table, where frame 20's EIAS was showing -8 but should've been -9 but everything else was correct. After making this change, this specific breakpoint adjusted to -9 but everything else remained the same. I see no inaccuracies created with this change in other things we've tested but I didn't look indepth.

Edit: maybe not it. That adjusts that Demon Crossbow breakpoint in your calculator to match mine with the 1 off issue, and it definitely isn't that (116 EIAS). It's definitely 117, which was achieved with double truncation like before.

ChthonVII commented 2 years ago

I'm starting to think that either a few of the observed data points may be off, or Blizzard did something really wild and complicated.

Let's start with the assumption that we're using the werewolf A1 animation with some adjustment made to the counter increment. This seems like a very good assumption because it comes VERY close to perfectly matching the observed data with a multiplier of werewolf_framesperdirection/human_framesperdirection. The question is: Where is this inserted and where is rounding applied?

(To account for werebear and sorcs and amazons and claws, it's probably really more like (wereform_framesperdirection human_animationrate)/((human_framesperdirection - human_startframes) wereform_animationrate), but let's get werewolf solved first.)

Now let's look at the troublesome datapoints:

Troublesome Datapoint # 1: Observed: 1HS normal attack -9 EIAS gives 20 fpa We need an increment value 159-166 (inclusive) to get 20 fpa from the werewolf A1 animation. Calculating the adjusted increment for werewolf 1HS with -9 EIAS with no rounding at all gives 159.39... So we're looking for f(-9, 1HS) = 159

Troublesome Datapoint # 2: Observed: 1HS Fury 144 EIAS gives 23 fpa (and 145 EAIS gives 20 fpa) Using the werewolf A1 animation with 70 rollback, an increment value of 426 gives 23 fpa, and 427 gives 20 fpa. So we're looking for f(144, 1HS) <= 426 and f(145, 1HS) >= 427

Troublesome Datapoint # 3: Observed: XBW Fury 116 EIAS gives 24 fpa (and 117 EAIS gives 25 fpa) Using the werewolf A1 animation with 70 rollback, an increment value of 358 gives 25 fpa, and 359 gives 24 fpa. So we're looking for f(144, XBW) <= 358 and f(145, XBW) >= 359

I can't find any way of calculating f() that matches all three.

I can match # 1 and # 3 with Truncate((Truncate((256werewolf_framesperdirection)/human_framesperdirection)(EIASplus100))/100) But that doesn't match # 2. (And # 2 is a kind of important breakpoint to make sure we have right.)

(Note: An additional reason to think this is plausible is that (supposedly) the old wereform code replaced the animationspeed term with different value. It's plausible that Blizz left that code in place and is just generating a new replacement value.)

Would you mind re-testing datapoint # 2 with the mana cost method?

[edit: That would also move the crossbow fury 30 fpa breakpoint from 80 EIAS to 81 EIAS.]

ChthonVII commented 2 years ago

New update. I went with Truncate((Truncate((256*werewolf_framesperdirection)/human_framesperdirection)*(EIASplus100))/100) in this version since that matches more of the troublesome breakpoints than any other option right now.

Warren1001 commented 2 years ago

Sorry, didn't see your first reply somehow. I tested the second datapoint.

I set Werewolf in skills.txt to give 135 attackrate while wielding a Caduceus to achieve 145 EIAS. I gave myself 610 mana and sustained permanently (mana cost/sec is 5 at 20 frames, mana regen/sec is 5.07 at 610 mana, more than mana cost/sec). I then gave myself 608 mana and lost mana over time extremely slowly (mana regen/sec is 4.98 at 608 mana, less than mana cost/sec).

I then modified skills.txt and set attackrate to 134 while wielding a Caduceus to achieve 144 EIAS. I gave myself 528 mana and sustained permanently (mana cost/sec is 4.348 at 23 frames, mana regen/sec is 4.395 at 528 mana, more than mana cost/sec). I then gave myself 526 mana and lost mana over time extremely slowly (mana regen/sec is 4.3 at 526 mana, less than mana cost/sec).

I looked at onderduiker's Frenzy tests and saw he was targetting the mana regen/sec breakpoint that was just above the mana cost/sec, so I did the same. Something I noticed while looking at his Frenzy data, his tests don't make much sense to me. He has the 5% chance that the first hit misses and the first frame length duplicates. But in some of his breakpoints, he shows an odd number for this, which should be impossible (any integer x 2 will always be even). If you read what I had written here before the edit, that's what I was confusing myself over.

Either which way, taking the exact same steps that onderduiker did, I confirmed 144 EIAS = 23 frames and 145 EIAS = 20 frames for Fury.

Edit: I tested the other two datapoints in the same manner as above. They were also correct.

ChthonVII commented 2 years ago

Sigh. So all 3 troublesome data points are confirmed now, right? Which leaves the other possibility: Blizzard did something unnecessarily weird and complicated. I'll keep working at it.


As for Frenzy, the part where you've got a wrong assumption is that the first swing length doubles on miss. It's actually ridiculously more complicated than that: The sequence animation is 17 framesperdirection with action frames at 4 and 9. On the first action frame, if you hit, the increment will be recalculated using a new value for skillIAS. (This has no effect if you're already at max Frenzy charges.) On the second action frame, the increment will again be recalculated using two new variables. First, if you hit, the skillIAS value is again changed. (Again, no effect if already at max charges.) Second, if you hit back on the first action frame, then the secondary weapon's WSM is used here. (Another bit of weirdness is that the recalculated increment value is applied to the action frame itself, in addition to subsequent frames.) So you're playing frames 0-3 at one speed, frames 4-8 at another speed, and frames 9-17 at a third speed. If you start your test at max charges so that the skillIAS changes drop out, then it simplifies down to frames 0-8 (9 total) at one speed and frames 9-17 (8 total) at another if you hit on the first frame, or everything at the same speed if you miss on the first action frame. What's new in 2.4? Based on some very limited testing, it looks like WSM averaging is removed (rather than just debugged) and replaced with EIAS averaging. So you get the same speed throughout once you're at max charges. In the course of working on this calculator, I figured out how the old WSM bug actually works, and it turns out my Frenzy calculator is doing some rounding wrong for some configurations with a weapon with odd WSM (e.g., colossus blade). Fortunately, they're the bad configurations that no one would knowingly use anyway. I've revised the calculator to fix that, but haven't gotten round to uploading it. Also, the revised calculator shifts one frame from the second swing to the first as compared to the current one, because the way TitanSeal's equations work would require using a frame at the end for which there is no artwork.


By the way, if you haven't already, you might wish to read this calculator's math.js. It's got a loooong comment about how the animation loop works, citing a whole bunch of sources.

Warren1001 commented 2 years ago

Interesting read. I can see why you're taking the different approach to the calculator. It may be becoming of the point where using the formulas makes it more difficult than just simulating the animation loop.

I can do some testing for how dual wielding works either later today or tomorrow and see if I can figure out the change made or generate enough data that it makes sense to you.

ChthonVII commented 2 years ago

Some further thoughts about the troublesome breakpoints.

This tells us two things:

  1. There have to be at least two truncations involved to make the second data point work out.
  2. We are never going to get one rule that works for both cases since the remainder is bigger on the first data point, but it tolerates less loss from truncation before sliding into the wrong answer. Therefore, there must be different rules covering these two cases.

But how?

There is only one thing I can think of. It's ugly. I hate it. It's so unnecessarily complex I don't see how a programmer could plausibly write it. And it has some really, really bad implications for how it would complicate gear selection. But it matches the data points, and it's all I've got: If we pull apart the terms composing EIAS, one of them is negative for the first data point (the "minus WSM" term). So we do TruncateToInt((animation_speed * EIAS_component * werewolf_framesperdirection)/(100 * human_framesperdirection)) and then repeat for each component of EIAS, and then sum them. If we assume truncation means "round towards zero" (see the comments in math.js about this), then one of the truncates is a ceiling() for the first data point but a floor() for the second data point. Aha! Different rules!

Tentatively, I broke EIAS into minusWSM, skill_and_gear_EIAS, and constant100 and ran the three problem breakpoints on a pocket calculator. They each came out correctly. I'll need to refactor the javascript calculator to try handling EIAS in this crazy way so we can see if it messes up any of the other tested breakpoints. The division of the positive terms into skill_and_gear_EIAS and constant100 was an arbitrary guess on my part. Figuring out which division of the positive terms (if any of them!) is correct will be a pain.

(Again, I really, really don't like this idea; I just don't have a better one.)

Warren1001 commented 2 years ago

I adjusted my calculator to apply your new idea: https://docs.google.com/spreadsheets/d/1I6o3QWzsqEzkuUAq9VRMwgy4YY1ZZcNO1fZM4HPabwU/edit?usp=sharing There's a handful of 'off by 1' issues.

Edit: in every case listed there, it's 1 too big. The EIAS values INCLUDE WSM (Caduceus).

Edit 2: Question, do you think, assuming this EIAS change is correct and just slightly off, this would impact all animations? Are we suggesting a couple of breakpoints across all animations might be off by 1 currently with current calculations?

ChthonVII commented 2 years ago

I tried out splitting up EIAS that way and got as many new off-by-one errors as it solved. So that's probably not it. But it did put me on the path of something that's really close -- splitting off just the 100.

So instead of:

EIAS = 100 + skill_IAS + DiminishingReturns(gear_IAS) - WSM
increment = TruncateToInt((animationspeed * EIAS)/100)
increment =[almost]=  TruncateToInt(increment * nerf_factor)

Do:

EIAS = skill_IAS + DiminishingReturns(gear_IAS) - WSM
base = animationspeed
delta = TruncateToInt((animationspeed * EIAS)/100)
adj_base = TruncateToInt(base * nerf_factor)
adj_delta = TruncateToInt(delta * nerf_factor)
increment = base + delta

Again, the trick is to make troublesome breakpoint #2 happy with multiple truncations, and to make troublesome breakpoint #1 happy by feeding a negative value into one of those truncates so it rounds up.

Unless I'm overlooking something, this only misses on two of the observed data points, the 30 fps Fury breakpoints for 1HS and XBW.

ChthonVII commented 2 years ago

And I think I may have it.

EIAS = skill_IAS + DiminishingReturns(gear_IAS) - WSM
base = animationspeed
delta = TruncateToInt((animationspeed * EIAS)/100)
increment = base + delta
increment = TruncateToInt(increment * nerf_factor)

Does this suggest some breakpoints maybe wrong across all animations? Yes, it does. Assuming this is right, when EIAS (not including the 100) is negative, the increment will be 1 higher than all IAS calculators say it is, which will very rarely change a breakpoint. Testing will be required to figure out where the -30 on sequence skills goes. I've got a theory that Blizzard may not have deliberately changed anything here. I think I can see how it might maybe be possible to go from the old behavior to this behavior without changing any code and simply changing from a compiler that thinks truncate means "round towards minus infinity" to one that thinks it means "round towards zero."

ChthonVII commented 2 years ago

Since it looks like Fury is finally correct, I want to close out this issue as soon as Warren1001 agrees.

I've gone ahead and created new issues for the unresolved questions that I'm presently aware of:

On a somewhat related matter, since you've put in a lot of work towards getting these numbers right, I think it's only appropriate to credit you as a co-creator of the calculator. Would you be OK with that?

Warren1001 commented 2 years ago

Yes, that's fine. I'll get to testing on the other issues whenever I find time. If it turns out formulas just don't cut it, I may have to borrow some of the logic from yours and apply it to mine :p or just abandon mine