-
Notifications
You must be signed in to change notification settings - Fork 409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reactive Armor Hardener resistance calculation incorrect #680
Comments
I know that the RAH calculation is not supposed to be completely accurate due to some limitations and the simple fact that you cannot completely simulate a combat situation. It's supposed to represent a rough idea of what to expect. However, these numbers do seem to be off. That being said, I know almost nothing on the RAH mechanics. @Ebag333 Thoughts? |
I'm 99% sure that this is not true. I've done tests with mixed damage types, and always found it adjusted to match the damage type not based of my base resists. Now, granted, all my fits tend to have fairly uniform (though not exactly perfect) resist profiles, but I'll make a point of testing this tonight. If this ends up being true, we should be able to do this inside the effects file (where we're already running the calcs). It'll be pretty fugly and a lot more complex, but doable. Also, if you're running tests and comparing your in game resists VS Pyfa resists, there is a few percent difference between the two sometimes. I suspect it's a rounding issue (Pyfa or EVE's rounding issue, who knows), but it also could simply be that the in game client is displaying it incorrectly (there's plenty of that with DPS and cap numbers, for example). So that can throw off expected results between the two. This makes it more difficult to troubleshoot issues like these, but I can setup a scenario where I'm getting shot at with 50/50 ammo (probably Void) and have either Therm or Kin with much higher resists than the other. I'll try and do that tonight. Another thing is that I've never seen the RAH in game adjust to sub 1% values. In fact, it seems to only adjust in 3% increments (probably to speed up how fast it adjusts, as it adjusts 3% per cycle). It also doesn't adjust exactly, for example when getting hit with antimatter (which is 41.7% therm and 58.3% kin) , Pyfa will show 25% Therm and 35% Kin, but the RAH (in game) will adjust to 30% Therm and 30% Kin...even though with 3% steps it should be 27% Therm and 33% Kin or even 24% Therm and 36% Kin. So CCP doesn't even follow their own rules some of the time. After all those numbers I need a drink. So, between:
I could see plenty of scenarios where it looks like it's adjusting based off base resists...but it's really not. @MrNukealizer If you can give me the following:
@blitzmann (Just so you don't think you can dump this all on me!) I suspect that there's some logic going on that takes the incoming damage profile and adjusts it to some sort of stepped values. For example antimatter is 41.7% therm and 58.3% kin, but the RAH rounds it to 50/50. Can you think of a fairly straight forward way to do this? I don't think we can replicate CCP's formula exactly, but maybe if we did a rounding to the nearest quarter? So AM becomes 50/50, while some (purely theoretical) damage profile of 29/61 becomes 25/75. I think that if we could implement this that we'd end up with much closer to in game behavior. I might start up a separate issue to track this. OTOH, I have seen things like the RAH being set to 3% therm, 57% kin, so this isn't universal. Grrr CCP. |
@Ebag333 It seems like most of your testing was under conditions that cause the above effect. I've found that when taking split damage from only 1 type of weapon, it will go with the even split and disregard the resistance profile. At some number of damage sources or types the RAH instead keeps track of the damage taken during the last cycle and adjusts resistances from whatever type(s) of damage you took less of into the type(s) that did more damage. That's why it often fluctuates between cycles: because it often slightly overreacts, and the damage taken during the last cycle varies depending on cycle time of the weapons shooting you.
The game doesn't show sub 1% resists, but you can tell it does by the rounding errors. The resistances shown can add up to 60%, 61%, or very rarely 59%. It adjusts resistances by up to 3%, but it can do less. Normally the overcompensation and variance in incoming damage for a cycle are enough to cause the full 3% change, but not always.
That sounds like a situation of one damage source doing split damage, causing the RAH to go to the preset 30%/30% or 20%/20%/20%. If you use drones too, it should correct the resistances. For example, a Cerberus with ungrouped launchers can easily force the RAH to correct itself: |
Hmm, on second thought, the rules for determining whether the RAH accurately compensates or does 60%, 30%/30% or 20%/20%/20% seem a bit more complicated than they did at first glance. When taking all 4 types of damage or being attacked by numerous enemies, the RAH responds appropriately, but it seems to bug out when only taking two types of damage. For example, I just shot that ship with a Cerb using half thermal, half explosive, and the RAH went to 30%/30%. Then I put out Warriors and it stayed the same. When the missiles had to reload, the RAH went to 60% explosive because only Warriors were shooting. Then when I started using missiles again, it stayed at 60% explosive and completely ignored the thermal damage. This will definitely take more testing to determine where it draws the line between using the simple algorithm and accurately balancing out resistances. Edit: It seems to reliably use the accurate calculation when taking all 4 types of damage or when being attacked by 5+ rats doing 2 types. More tests are needed to see the threshold with 3 damage types and how it responds to player ships and drones. The point remains though, you can't tell which mode will be used solely based on the pyfa damage profile when it has 2 or 3 types. Perhaps it would be ideal to have the option to choose between 30%/30% or 20%/20%/20% and the calculated pattern when the damage profile has 2-3 types. |
Okay, so did some testing. Test 1 Test 2 What's interesting about this one is it went initially to 0/0/30/30, THEN switched to the final listed above. What's especially interesting is that it didn't pick the two strongest resists. After the RAH adjusted, the person shooting at me put 5 hobs on me. Even though they were doing very little damage, the RAH wanted to adjust mostly to therm damage. So clearly the way to beat a RAH is to use a high volley ship, with 5 light drones. However, I have seen the RAH get "stuck" where it stops adjusting (until you stop and restart it). So what does all this mean?
So what about the way we handle it with Pyfa? Clearly it's not perfect. But without the exact code CCP is using for the RAH, it would be impossible for us to replicate it perfectly. Additionally, there are MANY edge cases for this that are going to create scenarios that would be impossible for us to calculate. The one scenario where we COULD adjust it is we could tell it to ignore the third damage type. At a guess, CCP is dropping the third listed damage type (alphabetically), but this is far from confirmed. It's not based on damage as it dropped the higher damage one. This isn't a perfect representation of what's in game, but it's vastly better than the previous implementation (which made RAH look far worse than a DCU in every scenario). Regarding your scenarios specifically, I suspect that the RAH doesn't care who is shooting, just the number of shots. So by splitting your launchers and using different damage types, you're going to emulate a number of people shooting, not a 1v1 scenario. At it's best, the RAH adjustment will only be able to emulate very simple limited scenarios, against a specific set of incoming damage type. Once you get beyond a very simple scenario, all bets are out the window and we simply can't handle those complex scenarios with where we're at now. So, for now, I don't think it's worth messing with. The formula is pretty simple and straight forward, and I'm worried if we start adding edge cases that it's going to quickly get unmanageable. @blitzmann unless @MrNukealizer comes up with something else, I don't think there's anything to change here. While there's some plot twists to the way RAH works in Pyfa, I don't think we have a reliable way to capture that. My vote is for keeping the formula as is, and calling it good enough. |
When taking two types of damage from one attacker it will always go to 30%/30%. That's quite reliable; the issue is that it switches to normal computation when there are more than a certain number of attackers.
The reason the RAH went to 0/0/30/30 first is because of the way it modifies the resistances. Every cycle it checks how much damage of each type you took during the last cycle, then it transfers up to 3% from resistances that took less damage to those that took more. I don't know how it calculates the amount to transfer to what, but the cycles go like this:
I replicated this test to the best of my ability using a target with 74.1%/87.8%/70.8%/83.6% resistances and an attacker with 300 DPS from Depleted Uranium and 94 DPS from Hobgoblins.
If there's a way to get the ship's resistances with all tank mods except the RAH and Damage Control, it would be an easy matter to calculate the resistance profile the RAH would converge on. As for choosing whether to use it or not, it's pretty simple except in the case of two incoming damage types. With one type it always goes to 60%. With four types, and apparently with three, it always calculates the profile to minimize damage taken. With two damage types, however, the formula depends on the number of attackers. Since that's impossible to know from the damage profile, that's a bit of a dilemma. For most PvP situations it would go to 30%/30% (if you're somehow only getting hit by two damage types), whereas for PvE it would almost always use the correct profile due to the number of attackers. Like I said, I can code it, but I don't know how to get the ship's resistances before RAH and DC. |
I don't understand anything in this thread, so you guys figure it out. :p @MrNukealizer, as for getting the ship resists after other modules but before DC, there isn't an easy way. You could pull it off by fucking with the effect runtimes (so that DC run after RAH, which runs after all other tank mods), but we only have three levels of runtime, and that might get messy with other effects that might depend on those effects. Alternatively, and this is really janky, but you could check to see if the ship has been "affected by" the damage control. If it has, do the reverse calculation that the damage control performed. You would probably have to set the RAH to I just thought about resist boosts via fleet booster. Yeah, you'll have to determine what to do there. YMMV All in all, it's my personal belief that the RAH is a complex module in the game, and cannot accurately be represented pyfa no matter how hard we try try. What we have now is a best effort type of thing. If we can better it, great, but I know fuck all about it, which is why I deferred to @Ebag333 :) |
Except you're defining "attacker" in a way that doesn't necessarily make sense. If you define "one attacker" as "one volley from one weapon, then...this is kinda true. If you define "attacker" as one person, who may be using multiple damage types (not even accounting for drones, which the majority of ships carry at least 1), then this is nowhere near to being true. It also won't go to 30/30 against stuff like say Scorch, which has very high EM, and some (but low) therm. (This I've tested). The estimated Pyfa adjustment for Scorch is 49% | 11%, and the actual in game numbers were within 2-3% of that.
Your numbers and logic look reasonable, but it's still simply a guess. We can't code of "this is the way it could be."
This is incorrect, and clearly shown to be incorrect in the drone + arty test. Thermal was my highest resist, and it was the lowest incoming total amount of damage (by a massive margin). The RAH adjusted almost entirely into thermal despite being the smallest absolute damage (and even continued to adjust thermal up even during the cycles when the Arty hits were landing, which there were plenty, and plenty of back to back ones). While the damage did bounce around a bit, the trend was absolutely clear. Kadesh (who used to maintain Pyfa) did their own testing on this, and reported:
(That is, using Warrior drones and shooting thermal missiles) Your own comment even confirms this:
Only this isn't a bug but a...uh....we'll call it "by design." Regardless, this is getting out of damage profile and into multiple damage sources territory, which is not (and probably never will be) covered by Pyfa.
This just isn't true. I've ran into multiple scenarios where the resists adjusted maximized my damage taken (given the incoming damage types). This is the primary complaint about the RAH, though the exact technical bits are often ignored for "it doesn't adjust the way I want it to."
Also doesn't seem to be true. Tested using multiple damage types with one attacker with split weapon (that is, ammo with multiple damage types), or multiple attackers. For every test I've done so far, incoming damage from a single source (not just a single attacker) only accounts for 2 of the 3 damage types. If you split up the guns/launchers (or add drones), then the RAH will treat a single attacker as multiple attackers. It still seems to follow the rule above (where individual volleys ignore one of the damage types), but the resists fluctuating makes it more difficult to track. The more sources of incoming damage that there are (and the more varying the damage type), the more easily the RAH gets "confused". To give an example, I used a ship with almost perfectly even armor resists. (1 attacker, 2 launchers) By your theory of "minimal damage" this is clearly bad, because the thermal is being adjusted too high and the EM far too low (by 14%). (1 attacker, 3 launchers) Again, by "minimal damage" metrics this isn't very good. While it's reducing the damage to the highest damage amount, it's completely ignoring the EM damage. (1 attacker, 2 drones) So again, not the optimal resists (not by a long shot). Not even the optimal resists based off the number of hits, since the drones were basically hitting one for one. What I did notice was that the RAH started out with all three damage types being set (though never hit that "optimal" resist profile), but slowly migrated toward the final one listed above. What has all this testing shown me?
Okay, enough debating about complex scenarios involving multiple damage sources. Whether or not it's theoretically possible to reverse engineer those scenarios is pretty much out of scope as it's going to be complex as all get out. Now, regarding a single source of damage (and ungrouping your missiles doesn't count), if you can show me a fit, ammo type used, and screenshot of the RAH resists and it can be replicated (not because I'm doubting you, but because RAH is complex and doesn't always work as expected), then we can look at adjusting the formula used. To be fair, your thought that it fills holes isn't unique. Ivy links a Reddit thread where this is claimed (though without any CCP confirmation). Ivy also claims that it adjusts in 6% jumps, when it's actually 3%, so there is that. I've tested the RAH against all four ammo classes now, and have had no evidence at all that this is true. I'm willing to entertain the idea if you can show me proof, but it needs to be a very simple scenario as that is all that we can handle in Pyfa. Now regarding the comments above by Mr @blitzmann , trying to run it to figure out a way to get resists sans DCU (and links?) is going to be virtually impossible to do without making it incredibly complex (and lots of points of potential error). We would likely be better off simply accepting the current resists and running off those, even if they are off slightly. As all the resists would be bumped equally by DCU/Links, the impact on the RAH adjustment would be minimal at best. It would amount to accepting a very small known amount of error versus lots of room for a completely unknown amount of error. Okay, and perhaps we can clarify things a bit more by listing out the scenarios that we support. Scenario: Single damage type Scenario: Two damage types Scenario: Three damage types Scenario: Four damage types Regarding the last scenario, theoretically since it's omni damage the RAH shouldn't change. If it follows the same pattern as scenario three, then the first two would get adjusted to 30. I'm not aware of any ammo/drones that use all four damage types, but have it uneven. Anything outside those four scenarios would be completely unsupported by Pyfa (since damage profiles can't really cover that either), so let's just stick with those four. |
Are you sure there was only EM and Thermal damage applied since the RAH was started? I just tried Scorch against a target with fairly even resists, one with much lower EM, and one with much lower Thermal, and in all cases the RAH split 30%/30%. I've also used Scorch in several other tests and IIRC they all went 30%/30%.
That's exactly how my test went. I have screenshots of the resistances for the first 20 cycles and they fit that pattern perfectly. I'm still not sure what the exact rules are for shifting the resistances, but it doesn't matter since the convergence point is where each type of incoming damage does the same amount. That's quite easy to calculate from the resistances and damage profile without simulating the exact algorithm.
I said that wrong. I was under the impression that matching the ship's resistance profile to the damage profile would minimize damage taken. After doing the math, I can see that's wrong. I'm not sure what to say about your test without knowing the turret/drone DPS of the attacking ship and what kind of numbers you got on the RAH. In my tests it has always responded properly to drones, including a Nightmare with a flight of thermal drones and Tachyon Beams, which have a low rate of fire.
Yes, it gets very weird when it's doing the two type calculation and you change things. That's not related to the way it works with 3+ damage types though, and there's no way to simulate changing damage profiles mid fight. It would be much more reasonable to simulate the behavior when taking the same proportion of damage the whole fight, which for two types seems to always be 30%/30%.
You're correct that it's not minimizing damage in this case, however you're wrong about why not. In that situation with truly even resists like on a Gnosis, it would be most effective to put all 60% in thermal and forget about kinetic too. It is however doing its best to equalize the damage taken from each type; your incoming damage profile after resists is 22.7%/39.8%/37.5%/0%.
I'm not sure where you're getting that, but I didn't notice any such pattern in your results.
Yes, because doing two types of damage does tend to cause a 30/30 split, ignoring a lot of things that affect the outcome against 3-4 damage types.
Here you go: http://imgur.com/a/4hGHH
Links don't matter one way or the other. The only reason I wanted to exclude the DC is because of stacking penalty calculations. If those will work correctly, it should be fine. @Ebag333 sorry for the wall of text. We seem to have a difference of opinion on certain bits though, and it's important we both understand how it works. |
Yup, and this is what makes RAH so much fun. Tests that are replicated do not always give the same results. (I tried to replicate your results with missiles, for example, and got different results.) I suspect that getting stuck at 30|30 isn't actually intended.
I'd love to fully understand how RAH works, and I've learned several things about it that I didn't know before while testing it. Unfortunately, unless someone figures out how to extract the relevant code from the client, or CCP posts the logic publicly, we're left with trying to reverse engineer it, which is sketchy at best. Here's where I see that we're at:
I don't think there's an easy solution here, because of the various "features"/bugs with RAH such as:
Clearly the solution in place is better than what we had before. It's not perfect, and not 100% accurate, but it's never going to be. As the smart person in this conversation said:
|
Ok, I think I've figured out the exact logic. At the end of each cycle the RAH looks at how much damage of each type was taken during the cycle, after resists. Then it does one the following:
That's why it locks at 30/30 when taking two types of damage. The two resistances taking the least (0) damage evenly shift into the two taking the most. Those two resistances never shift between eachother because it doesn't shift resistances away from either of the two taking the most damage, and they're always taking more damage than the two resistances not getting hit. It also explains the bit about the RAH shifting to 60% when taking one type of damage but not adjusting when a second type is added. The type with 60% resistance may be taking significantly less damage than the other, but it's still one of the top two and won't shift resistances away. I redid my test involving shooting the Megathron Navy Issue with Depleted Uranium ammo, and it turns out that adding even 1% more thermal resistance causes the RAH to lock at 0/0/30/30. With the resistances I had for the first test, the thermal resistance was just barely low enough to take more damage than explosive and cause a shift to 0/3/33/24, but a slight increase in thermal resistance is enough to keep kinetic and explosive as the top two damage types and stop them from optimizing between eachother.
I tried this setup with a Gila shooting a Gnosis. The Em/Therm balance behaved as expected, but the randomness in damage between hits made it very hard to predict the balance between EM/Therm and Kinetic damage. The RAH generally bounced around near 30/0/30/0, often locking in for several cycles until one drone got much better hits than the other. I wouldn't be too surprised at your result of 10/0/50/0 if the Vespa got significantly better hits than the Infiltrator for a couple cycles, because kinetic would almost always be in the top two damage types and thus never go down.
I did that with a Gecko hitting a Gnosis, and the RAH kept switching between 15/15/15/15 and 9/21/21/9. Against other ships it evens out the resistances as expected. |
This is all good testing, I appreciate the time you've put into this. I'm doing some additional digging myself on this, so we can leave this open for a while. |
Confirmed by Unfortunately, despite all the testing we've done, I don't think we're necessarily any closer to figuring out the actual behavior. While we've definitely nailed some aspects (such as the RAH only adjusting for 2 damage types out of 3/4 damage type volleys), the bigger question is should we implement those? If the damage profile is a single source, then absolutely. Problem is, there's nothing that enforces that, so if someone creates a damage profile with a fleet battle in mind, it no longer behaves as expected (even though we don't necessarily support this scenario, there's nothing to stop users from using it this way). You have good arguments/tests showing that the RAH very well may operate in a way that takes base resists into account. Unfortunately, there's also scenarios when it clearly doesn't. While the solution we have in place isn't ideal, unless someone can find the actual logic behind it I don't foresee us being able to implement anything better. There are too many use cases, and too many "well, when you do this" scenarios. (And by actual logic I mean actual logic, not simply "here's a scenario where it behaves as my theorycrafted logic says it should.") We could make it a lot more complex and start attaching options for "single damage source" vs "multiple damage sources," but then we need to explain to a player that his ship fit with turrets and missiles and 5 drones (with 3 different sizes/damage types) doesn't count as a "single damage source." TL;DR: RAH is a largely unknown module with complex mechanics, and what's in place today counts as a best effort. |
I always love a good discussion, and encourage it continuing here. However, for now, the RAH mechanics within pyfa will stay the same unless some sort of logic can be figured out that makes it more useful. It was never meant to be accurate per se, but rather to give a better idea of how it might settle instead of giving 15 across the board. It is a best effort calculation. Again, I defer judgement on the formula to @Ebag333 as I know fuck all about it, and this thread has already turned my brain to mush. Please feel free to continue discussion; if anything this is good information for others who might be googling how RAH actually works. =) |
Hi Space Friends, The RAH works out all the damage you've taken at the end of each cycle. This is damage after resistances are applied. So the same damage you see on your logs. It also only counts Armor damage, ignoring shield/hull damage. It only counts the damage taken since the start of the module cycle. If you've taken 1 type of damage, it will lower the other 3 attributes to buff that 1. I hope that clears things up a little :) Cheers, |
Chatted with @ccplarrikin some in Slack. There were statements made that make it seem to me that it's not quite as cleared up as I would have liked. :) BUT....As I understand it....it does not adjust to fill your resistances (it doesn't care if you have 99% EM or 1%). But it does adjust in the damage calc post resists. So order of operations is:
This makes sense why people swore that the RAH adjusts to fill your holes. It doesn't. But your holes will leave the most incoming damage left for the damage types you're weakest in, and remove the most damage for the types your are highest in. So, if we use a theoretical example of resists of 75% Therm and 25% Kin, and incoming damage of 100 Therm and 100 Kin (ignoring all the special exceptions for the moment), it looks something like this:
Okay, now for special exceptions.
So. How do we handle all this? First off, officially we can only support a single source of damage, so we can ignore exception 2 entirely. Now, by that metric we should follow exception number 1. The problem is that there's nothing stopping people from entering damage profiles for multiple damage sources/types, and we can't simply assume that a damage profile is a single volley from a single weapon. I believe that if we followed exception number 1, it'd confuse the heck out of a lot of people, and it'd also be completely broken if the damage profile being used was for multiple incoming weapons/drones/etc. (For example, someone doing a Worm damage profile with EM drones and mixed therm/kin rockets would adjust completely wrong, as the EM drone damage would be entirely ignored even though it'd be 75% of the DPS.) So to walk the tightrope, I think our solution is to ignore exception number 1 entirely, and simply move the RAH calculations to after the armor resists are taken into account (or rather, we use the armor resists to appropriately modify the damage profile). This means that the RAH won't be right for single damage sources with 3 or 4 damage types, and it won't be right for fleet fights....but it'll handle single/double damage types fairly well and be a fair approximation of fleet fights. And finally....
Well....crap. This is where things get complicated. Assuming (and this is a pure assumption on my part, no CCP confirmation here) that RAH applies after ALL the reductions, then sig and speed would play parts. Fortunately for me, Pyfa doesn't account for this one bit. So I can safely ignore any potential other reductions, and just worry about the reductions from resists. (Unless someone can think of another reduction that would come into play?) I'll play around with the effects file and see if I can get it to play nice with the logic above. |
That's about right, but with a couple clarifications: 2: AFAIK the other reductions can be ignored completely since they affect all resistances evenly and don't have stacking penalties with modules. 4: That depends on your definition of "match that damage profile." It does adjust based on the incoming damage profile post resists, but the "match" part can be complicated due to the way it refuses to shift the resistance taking the second most damage into the one taking the most.
In that case it would adjust to 0/30/30/0 because it shifts from EM/Exp to Therm/Kin but never reduces the thermal resistance because it's taking the second most damage.
Calculating after armor resistances does seem like the way to go, though I got to thinking: After learning more about the way the RAH works, it doesn't seem like there's a simple mathematical solution. Thus the RAH cycles would need to be simulated till it stops changing or enters a loop. That should never take more than 5-10 cycles, but I have slight concerns about handling the stacking penalties with Damage Controls or performance issues recalculating the whole ship's resistances. There's also the issue of what resistances to use if it enters a constantly cycling loop like it did for a few of my tests. I'd probably go with the average of the profiles it cycles through, but I have a feeling the effective resistance profile over time is slightly more complicated than a simple average of the cycles.
It would be accurate for a single damage source with 3-4 types as well as for fleet fights. There's no difference in performance based on the number of damage sources, except where the weapon timing and RAH cycle time, or variations in hit quality, cause fluctuations in the damage taken from each source. Since pyfa can't take that into account, there's nothing to be done there. |
This is not the way the formula works, as I understand it. To be fair, there's a bit of a telephone game going on here. I haven't seen the magical formula with my very own eyes, but I have talked with CCP Larrakin while he was looking at the code. (He's also very well aware that there's further conversation going on in this thread. :) ) Now, that's not to say that there isn't a bug or other issue, but that's the way it should work. (Some caveats below, but close enough for our purposes.)
I don't believe that this is true, as I ran into this with projectile ammo doing 3 damage types (and while it was nearly even starting resists, it was certainly not even damage done). Now your order of which it prefers may not be exact, but the point was there there is something at play that 3/4 damage type volleys only seems to account for 2 damage types. But regardless, this is something that we can safely ignore for the reasons listed above. So no need to further beat that particular dead horse.
And CCP Larrikin agrees with you. I believe I did list the resists (if I didn't, it was a Strat with resists that varied by less than a percent, so about as even as a non-Gnosis can get). I did put in a bug report about this, as even going back to the logs and adding up the damage, looking at times/cycles, it doesn't quite add up. This is one of the better known/documented "problems" with RAH, there's a good number of threads/posts about it (in fact it's one of the very first things @blitzmann pointed out to me when arguing not to open the can of worms by making it reactive, because of testing Kadesh did back in the day). REGARDLESS, I will shamelessly quote myself:
Moving on.
Well, CCP makes it work with a simple mathematical solution. It's just everything around that mathematical solution that makes it hard, as we keep getting caught up on edge cases and probably buggy behavior. (At least half this thread is stuff that's very clearly
This goes back to the limitations that we have. Simply put, we cannot have the RAH simulate anything more complex than a single volley from a single weapon (at least officially, unofficially we try and will get mostly right). We absolutely cannot simulate the RAH behavior over time. This means we can only show the a simplistic representation of RAH values, and not how it behaves over time. There's no unofficial try on this one, but it's no different than many other values we expose, such as passive shield HP/s or your cap regen GJ/s. These only show your peak values, and not the whole range. You are absolutely correct that the RAH will be "slightly more complicated than a simple average of the cycles" for mixed damage. For one, averages have nothing to do with it, the RAH (in game) has space Alzheimers and forgets about anything longer than 1 cycle ago. What we are simulating with the RAH is a single weapon hitting the RAH with no stepping in play. The reason it goes through a cycle is because it's forgetful. To use some numbers as an example: Take the following base values: Assuming you could take the stepping out of the equation and have it fully instantly react, the behavior would be as follows: Shot 1 Shot 2 Shot 3 You now have the cause of the RAH "bouncing" or "cycling" in game. Because of the stepping that they do, it often makes it impossible to finalize on a single value,
Remember that this is a very simplistic representation of the RAH. You are absolutely correct in that it won't handle stacking penalties the way it does in game (though those are fairly minimal, to be honest, as you can't stack more than 2 deep). It also can't show the final final FINAL (really I mean it this time) RAH adjustment. What we will be showing will be what the RAH would do, if there was a single volley, from a single weapon, and the RAH could instantly fully adjust to that without any stepping/cycles in play. Man, I love that we actually have it that well defined. To use the example above, the RAH in Pyfa (with the yet-to-be-written future formula) would show a resist profile of 0/45/15/0, when the actual resists (once it finished bouncing around) would be more like 0/42/18/0 (I didn't actually play out the scenario to the end, but close enough). Is what we show (0/45/15/0) perfectly dead on? Nope.
There won't be any (further) performance hit because we're not going to be doing multiple cycles. While it's certainly doable (it's just a loop) it still won't take into account things like stacking penalties, and it's terribly complicated to build/maintain/troubleshoot for what amounts to a handful of percent difference. |
The actual functionality is iterative, thus it's hard or impossible to reproduce with a simple formula using the resistances and incoming damage. If you think of it as one step like that, it's no wonder there seem to be so many edge cases and bugs. If you instead evaluate the logic in a loop until it starts repeating, it's possible to perfectly simulate it for the assumed constant stream of damage with no variance or timing issues. Real performance would vary slightly based on the timing and quality of hits, but the damage profile doesn't say anything about that so it need not be considered. With the logic I mentioned a while back and will repeat below, the only "edge case" that needs consideration is when taking only one type of damage instead of 2+.
Sure we can simulate the RAH behavior over time. Python can do loops, and it's fairly trivial to simulate the 3-12 cycles the RAH would go through before it stops on one profile or starts repeating. Since we can only use one value for the final RAH resistances, that would be either the profile it stops on or the average of the few it loops through (the effective resistances across cycles are actually a simple average).
Ok, the way I see it, the damage profile is the amount of DPS that's constantly hitting your ship for as long as you're in combat, not one single hit. If you're getting hit by constant damage for more than a minute or two, the RAH will either find a profile where it stops changing, or loop through a repeating series of profiles. If it stops, that's the obvious resistance profile to use. If it loops, the effective resistances over time are the average of the resistances during each cycle of the loop. Some cycles you'll take more of one type of damage and less of another, but by the end of the loop it all evens out. Given the assumption that the damage profile is constant DPS hitting you till the battle is over, the RAH would continue to loop an undetermined number of times, evening out the effective resistances to the average of the profiles it loops through.
That's a nice scenario, but the real RAH would just go to 0/30/30/0 and not change any more, so using a simulation like that would be quite inaccurate.
Given an indeterminate amount of time, normally under 40 seconds, the RAH will always repeat the same few cycles over and over again. While it would be pointless to use any one of the values it cycles through, if you get hit by the same damage during each cycle the effective resistances would be the average of all the profiles it cycles through.
And that will be inaccurate 100% of the time, occasionally getting close but never actually correct. Just find a way to get the ship's resistances with all tank modules active, and I'll provide the code to give an accurate value. The logic is as follows and very easy to implement:
I literally just did that in about 30 lines of C# code (it would be a lot less if it was possible to use references or pointers in that context) for another project and it perfectly matches every test I've recorded. |
Hi, Ok, I've done a bunch of tests on this. I added some logging to the RAH to show exactly what its doing every cycle. In all cases the testing procedure was the same. Drones/Weapons were ordered to attack. After a short while, the RAH was activated. Scenario 1Vagabond (5x 650mm Artillery Cannon II w' Quake M, 3x Gyrostabilizer II, 5x Hornet II) shooting a Gnosis (1x Reactive Armor Hardener, hacked up armor HP).
Scenario 2Vagabond (5x 650mm Artillery Cannon II w' EMP M, 3x Gyrostabilizer II, 5x Hornet II) shooting a Gnosis (1x Reactive Armor Hardener, hacked up armor HP).
Scenario 3Vagabond (5x 720mm Artillery Cannon II w' EMP M, 3x Gyrostabilizer II, 5x Hornet II) shooting a Gnosis (1x Reactive Armor Hardener, hacked up armor HP).
The RAH is working as intended. The rule of thumb for the RAH is as follows >
The damage is calculated after resists. I hope that helps! Cheers, |
Ok, this is starting to get a bit frustrating. I've been saying things for over a week and you just disregard them until Larrikin says the same thing, then you still disregard the part I said but he didn't.
Moving on,
Or perhaps the RAH can overcompensate for one of the top two damage types and let a third surpass it for a cycle.
Keep in mind most people don't have Resistance Phasing V so the cycle time could easily be 6-10 seconds. Also when getting shot by multiple people who don't coordinate their fire, there's a much higher chance of getting hit by the weapons every cycle.
Please hop on SiSi with this fit:
Then shoot it with a projectile weapon using Depleted Uranium ammo. That does three types of damage, and it most certainly doesn't stay at 0/0/30/30. It goes to 0/0/30/30, but then the thermal resistance is low enough to take more damage than explosive, so it loops between 0/3/33/24, 0/0/34.5/25.5, 0/3/37.5/19.5, and 0/0/39/21. |
Disregard? No. Did I pay more attention to the person who was looking at the actual code (and hacked it to add debugging)? Yes.
True, and there's other edge cases where changing resists will change the incoming damage and change which damage types are the top two. There's nothing we can do to accommodate that, however, as we can only deal with fixed values.
It's more an order of operations thing. The point is that the RAH subtracts resists from the weakest two resists, then adds the amount subtracted to the highest resists. (Equally.) Everyone thinks of RAH as trying to add to the highest two resists, when in fact it's subtracting from the weakest two (and then just dumping whatever extra it has). It's a subtle difference, but explains exactly why these various scenarios that we ran into happened the way they did. It might be a subtle difference, but it's insanely important.
And in mixed damage scenarios this absolutely happens. It's actually the normal damage stuff that tends to cause this more than the resists changing (as damage fluctuates quite a lot if not using missiles, and even if using missiles with speed/sig). However, either one of these scenarios requires multiple cycles, which gets into times, complex values, and other things that Pyfa simply cannot handle.
And again, mixed damage scenarios are something we cannot account for due to the vast complexity of Pyfa. It may be able to cover some of it through the use of the projected tab...but it still wouldn't cover large chunks of it.
This absolutely happens. It's also not a scenario we can cover because of the timing/cycle issue that Pyfa cannot handle with the current engine. |
Ok, I took the file in your pull request and modified it to use the algorithm I've found to be accurate. Now it's just up to @blitzmann to choose which to use. |
Gonna open this again as there are now pull requests associated with this discussion. I'll be honest, I still don't know what you guys are on about, but I'll take a look. From what I gather, cycling the RAH causes issues because each cycle will change the incoming damage. However, RAH cycling isn't currently supported in pyfa. I was discussing the idea of doing some sort of cycle like we do with cap simulation to find some sort of average, but it's not something that's on the radar for now. I'll take a peak at both PRs. Please do not expect either of them to be merged any time soon due to RL being very busy and no easy way for me to quickly test these in game. =) |
Just realized that you actually did implement cycling into your code. Well done! I don't like that it's in the effect file itself due to performance reasons, but if it works well it is not a big deal (considering you can only ever have one on a ship, so this effect file will should only run once per fitting calculation) |
As we discussed on Slack, it runs 3 times for the fit when its opened, once each time it's refreshed (which is every time you click anything at all), and once per booster. (Boosters only seem to run once regardless of open vs refresh.) So theoretically, worst case scenario, it could run 6 times. |
It doesn't seem like the cycling would cause performance issues, but it can be optimized a bit if it does. I'm not sure how efficiently Python runs it, but very similar code in C# could always resolve in under a millisecond. If it does need to be optimized, the number of damage types could be checked before the loop to handle one or two types in one step, since those always go to 60% or 30%/30%. I also think I probably did some stuff inefficiently since it was the first time I've touched Python. |
...And now that I think of it, I shouldn't have left the logging in for my pull request. The code itself should perform fine, but the logging might slow it down a lot. |
Naw, logging wouldn't usually affect performance as I believe it's async. Could be wrong, but I've never had performance issues because of logging. But yeah, if this were to be merged, I'd take the statement out / comment them out. |
We need more logging in Pyfa. Almost nothing gets logged, so when we have a problem, it's difficult to troubleshoot (especially when we can't replicate it). As long as it set for debug and not info, it only pops up if debugging is turned on, so the impact is incredibly small. It's not the debugging. And while the code could be optimized, at the heart it's not really the code. It's the inefficient nature of Pyfa, and the extremely complex fitting engine behind it. Unfortunately this sometimes means that we can't do things. There are a few things I've tried to implement, but had to put aside because of the impact it had. Pyfa already runs very sluggish, we simply need to be very careful about what we add. |
Thinking about this further, I would suggest that we look at merging RAH v2, but leave the v3 PR open. v2 is closer to real life scenarios than the current version, so it's a step in the right direction. When we can get the v3 code optimized enough to bring it down to sub second times for that initial open, and/or we can optimize the code around it, we can have it ready to drop in. That won't be today or next week (unless someone comes up with a brilliant plan to optimize the RAH code), but it'll be waiting and ready to drop in, and we can continue to iterate on it as we go along. |
Ok, I made a couple minor tweaks to the v3 code and commented out the logging, and it seems to run in a millisecond or less. Toggling the RAH in debug mode seems to change the time spent in the "late" runtime from 2-3ms to 3-4ms. I can personally (sample size: 1 on a good machine) load a fit with an RAH, three different boosters with RAHs, and two projected fits with RAHs, in well under a second:
@Ebag333 please do your testing with the new version and see if it's still sluggish. I have the distinct feeling it was the logging causing issues, since my logs were being written to an SSD and I had no such issues. |
@MrNukealizer @blitzmann I completely rewrote the function from the group up. It seems to run much faster now, and will loop through to dynamically adjust (so we catch the edge scenarios where the lowest damage might switch). Seems to run in a couple hundred milliseconds, even for fits that have boosters. If you want to test it: |
@blitzmann any chance of merging v3(#689) or v4(#737) soon? Both work well at the moment and are significantly better than the current RAH implementation. Below are the remaining concerns that may need to be dealt with:
|
I still need to review the changes, but I will definitely merge one or the other with the next patch update. (yes I'm procrastinating :P) |
Closing discussion as I've finally merged one of the PRs. Feel free to continue discussion about it here if desired. :) |
In pyfa the Reactive Armor Hardener always sets its resistance profile equal to the incoming damage profile. That's not how it works in game though.
In game the RAH adjusts to match the ship's overall resistances to the incoming damage, rather than matching its own resistances. What this means is that when taking 50% EM and 50% Thermal damage in a ship with 80% EM resistance and 50% Thermal resistance, the RAH will converge on 0% EM and 60% Thermal, not 30% EM and 30% Thermal. Another example: when taking 25%/25%/25%/25% damage in a ship with resists of 60%/50%/40%/30% the RAH will converge on 0%/5.8%/21.5%/32.7%, not 25%/25%/25%/25%.
There are certain case with one or two attackers doing mixed damage where the RAH will shift to 30%/30% or 20%/20%/20%, but it's not possible to figure out whether that will happen based solely on a damage profile, so that can be disregarded.
I would fix the calculation and put in a pull request, but I'm not familiar enough with pyfa's codebase to make it work. The calculation would need to know the ship's resistances with the effects of all other modules except Damage Control, and I'm not sure how to get that.
The text was updated successfully, but these errors were encountered: