just to chime in quickly because you mentioned burst duratiin. from what i know this is always at least 1 frame, even if can_burst is 0), as is aim time. hence even if you set all delays to 0 (for a ballistic weapon at least) the maximum number of shots per second is 4, or one every 2 frames
Yes, that's one way to explain it. In hindsight it maybe is the more elegant one. I started testing single fire weapons first and since the editor says they don't use burst at all, I neglected all burst stats. After that, the easiest explanation was to add 0.125 somewhere, which I then added to the fire_aim time.
I will think about this later and maybe change the formulas/descriptions.
Did you make some tests after the 64 bit update? I still find it odd that a game that is based on tick rate doesn't care about it for many of its stats and I somehow suspect the update to have changed something.
Ok, so if I understood you right fire_aim_time, wind_up, wind_down should be divisible by 0.125, or it's ried to in-game time, e.g. if fire_aim_tim finished in .387 fraction of a second it will be prolonged to 0.5 in real time? Also cooldown and burst duration can be anything and are not affected by the tickrate?
wind_up and wind_down get rounded up to the next 0.125 step OR 0.125 added if they are already a multiple. Fire_aim time seems to be different for single and burst fire weapons. For single fire, you add 0.125 seconds and take that regardless if it is a multiple of 0.125 or not. Burst weapons do not add anything and just take the random value.
As an example:
wind_up is 0 seconds -> 0
wind_up is 1 second -> 1.125
wind_up is 1.1 seconds -> 1.125
single fire weapon:
fire_aim is 0 seconds -> not tested, according to the attribute editor it can't be smaller than 0.125 iirc
fire_aim is 1 second -> 1.125
fire_aim is 1.1 seconds -> 1.225
burst fire weapon:
fire_aim is 0 seconds -> not tested, according to the attribute editor it can't be smaller than 0.125 iirc
fire_aim is 1 second -> 1
fire_aim is 1.1 seconds -> 1.1
There is a second way to describe this that on one hand streamlines the logic, on the other hand introduces new logic errors. But it yields the same result.
If you export the weapon_report table from the attribute editor, single fire weapons have a burst duration of 0.125. In the case of single fire weapons, you could use the same formula as burst fire (that is, do not ad 0.125 seconds to fire_aim but add the 0.125 of the burst duration. This gives you the same result). On the other hand the attribute editor says that single fire weapons do not burst and therefore do not use any burst stats (CanBurst is set to 0).
So congrats to whoever at Relic wrote this, it is outdated and potentially has always been wrong. Or it has been right and the game treats fire_aim times of different weapon types differently.
What exactly happens in the end does not really matter. What matters is that the formula models what we see.
awesome work man! from the limited testing i've done in the past to match my own dps calculations with the in-game behavior i know this is a very tough nut to crack. kudos for taking the time and figuring out the details, this will surely be of immense help to achieve proper unit balance (and hopefully also balance discussions).
i've got a question with respect to the formula; how does your code handle situations where delays (i.e. aim time × distance multi) would get below the 1 frame limit (which seems to occur frequently in serealia's data)? from what i've tested so far these delays have a minimum of 0.125 s and increase by multiples thereof, which i guess is what the apply add_125 function does?
Sorry for stupid question, but does all this mean that in order to calculate the correct dps you need to add 0.125 sec to every delay stat during shooting, except reload time, if it is bigger then 0?
e.g. fire_aim_time + wind_up + wind_down + cooldown +0.125*4
The game uses ticks of 0.125 time. For the game the only times that exist are 0.125, 0.25, 0.375, 0.5
I'll sum these up slightly since they target similar issues:
MMX:
I have not tested what happens below 0.125 seconds, simply because I did not think about it. For my testing I made Gren K98, Conscript mosin and Penal SVT (and both Axis HMGs respectively for burst weapons) carbon copies and then slightly altered stats to see what is happening. Once I had the model I compared the formula with some weapons as in the game and it seemed to fit (considering I cannot fully control distance within the tests). But you are right, maybe one more test is needed in that regard.
Kliement & Vipper:
The tank gun tests I did back then showed exactly this behaviour: Always round up to the next 0.125 step, but add at least 0.125. But either small arms behave differently, or the 64 bit update changed something. The stats for small arms all work differently. Some show the rounding behaviour, some show a simple 'just add 0.125', others stay as they are. Even if they are not a multiple of 0.125. A 1.3 second delay is possible for cooldown, on the other hand a 1.3 windup will always become 1.375. I have no idea why. I noted down how every single stat behaves in the stat overview spoiler. At least this is what my tests got me.
I have 1 more question:
What to do when burst length x rof is not a whole number.
A weapon can't fire say 17.5 rounds in a burst right???
As I wrote in the text, I am not sure what exactly happens in the game. But I have seen the weapon apply two different amounts of damage in successive bursts when I forced this situation. I am not sure if this fraction carries over to the next cycle or if there is a random pick. My best approximation is to just go with the fractional bullet and therefore fractional damage.
def correct_time(val):
if val>0: return math.ceil(val*8)*0.125
else: return 0
well yes I could have used the ceil function. However you shortened your version a bit too much because multiples of 0.125 get 0.125 added as well (for example, inputs of 0.125 and 0.2 must both generate 0.25 as output). I am happy if you have tips, however I'd rather move those to PM since code discussions are probably not very interesting for the other users.
Unless you spot a real error of course, then just post the fix or a fixed file here.
Don't take this the wrong way but as a software dev some of the code just looks so inelegant.
No worries. I assume it is for a professional, but I then again for someone that taught himself programming in the evening with no real need for readable coding in the job that is excusable.
I have a python script that uses the old formula. If you tell what the adjustment that need to be made I can modify a line or two of that code in the mean time to check.
Well then I'll just hand out the pre-version right away.
This is a zip file containing the weapon_report as exported from the attribute editor (thanks to MMX for providing this since my table program fucks it up) and an ipynb file with the necessary code (I used jupyter notebook for that). Needs pandas, numpy and plotly.express libraries installed (most recent version, older ones should work too). You can also use seaborn instead, just change the last cell accordingly.
Yes the game use 1.25 tick and timers use that tick.
Thank you very much.
I knew that, this is also what I suspected back in the tank gun tests and then termed "correction time". I thought the game needs to "wait" until the next tick when the previous part of the firing processed finished.
Things here are more complicated though. Some stats still use this correction time, others add a plain 0,125 seconds (so you can go from 0,4 to 0,525) and there will be no correction time at all. And in a third case nothing is applied. I don't know if the 64 bit update changed something, one would need to test tank guns again. But in the end it does not really matter that much. In the case of fire_aim time, this also differs between burst and single fire. Maybe my model that's not exactly what happens, but at least it is one way to describe the outcome.
Currently, the only source of DPS for small arms fire is Cruzz’ formula (also see Vipper’s guide), as well as coh2.serealia.ca which is an updated version that apparently uses Cruzz’ formula as well. However, no one knows where this formula came from.
When I tried to re-calculate these values on my own, I was unable to replicate the exact same values. Since this error could become quite large. I did some more digging only to find out that the formula does not match what I saw in-game.
Some time ago when I tested tank cannons, I found another probably game engine-related issue that increased the time between shots by 0.125 per used stat (I will call this 0.125 addition ‘correction time’ from now on), therefore I expected a similar issue with small arms. I then started iteratively modding and things turned out to be more complicated than that, but finally I can present a model that:
1. Matches the in-game time for shot cycles and
2. Based on that calculates the correct DPS of a given weapon.
I’ll spare you the lengthy details, in essence I had to test every stat separately to see how it behaves, changed it in a mod and then measured the time in-game by hand. If some of you wish to see the raw data, I can provide it but warning ahead it is quite convoluted. "Readability” was not an aim since I initially thought it would only be a quick notation sheet.
Additionally, one limitation is that I had to use "fixed" stats, meaning every stat was defined. The game however often uses randomization for some stats. Although I am fairly certain that I checked for all or at least most of this and my new model reflects this randomness, I cannot exclude with 100% certainty that this will lead to minor differences in the game and in my model. However, these differences will be minor, so I am confident to say that the new formula is better than the old one in any case.
The following spoiler defines a couple of “functions” that are necessary to understand the final formula:
Single fire weapons:
Stat overview:
The easiest way to calculate the DPS is to calculate the expected damage done and divide by the time for a complete shot cycle. Therefore, all variables that are randomly picked from an interval need to be taken as the mean between the interval borders. For brevity and visual clarity, I won’t note it down in the formula.
Expected damage:
Note ahead: Chance to hit and chance to penetrate are capped at 1. The braces are only there to group related stats and not needed for calculation
This means the model cycles through (fire_aim_time + wind_up + wind_down + cooldown) until the last bullet left in the magazine. For the last bullet, no cooldown is applied and the model directly enters the reload animation.
I have seen that the model also sometimes has intermediary animations (looking left and right, cowering etc. This does NOT affect the timings.
Burst fire weapons:
This is were things get tricky mostly because we get some additional stats.
Stat overview:
The reasoning is the same as before: Expected damage divided by the time needed.
Expected damage:
Chance to hit and chance to penetrate are capped at 1. The braces are only there to group related stats and not needed for calculation. I only have limited data on the ROF*burst_duration, but from what I saw this value is not rounded down. If a “half bullet” is calculated, there is either a random check of the bullet is given or not or it is carried over to the next cycle. I assume the latter, but not sure.
Further comments:
The old formula adds the ready_aim time. This is false. Ready_aim time defines the time to focus on a new enemy and is applied only ONCE at the beginning of the fight. It also subtracts the fire aim time. This is false too.
Aftermath
(I'll quickly add another paragraph to give some perspective)
So, what about all this. Overall this doesn't sound like a huge change, does it?
Depends on the weapon you look at. Literally all DPS profiles in coh2.serealia.ca are incorrect. Some values are still close to each other, others are not. For example, the Ranger M1A1 carbine is shows as having 10 DPS in serealia at range 0-4. My in-game tests however showed something closer to 8 (39-40 seconds needed to kill 4 Partisans with in total 320 HP at point blank which leads a DPS of 8-8,2. This leads to a difference of 20% in this rather extreme case. My formula calculates 8,207 which seems to be way closer to the in-game situation. In serealia, the Tommy Bren gun starts at 4,1 and ends at 6,9 DPS, my formula calculates 4,5 (+10%) and 6,95 (+0,1%) respectively. Or the Gren K98 goes from 5,99 to 2,26 DPS, while the new formula returns 5,27 (-12%) to 2,16 (-4%). We're currently in a state of the game where things are in general decently balanced, every move we make now can naturally only be a tiny step. If we want to make further changes, we need to get the correct numbers. I checked some few weapons in the game and my model is consistent with timings that I measured in game and some TTK data I gathered. I encourage everyone to do some measurements themselves and also report back what they got.
I’ll leave it at this for now, it is quite a wall of text already. I spent a lot of time on this, if anyone has doubts about my formula I would kindly ask to gather some data yourself. I will be all ears for discussion, but after countless cycles of changing stats and stopping times, I would like to have a break on this.
I hope this helps for the upcoming commander patch. Release version can be found here.