Okay. So effective tick rates in Halo are weird. But completely explainable.
At a tick rate of 30 tps (ticks per second) which is what OG Xbox ran at because it was 30fps (frames per second).
input_firerate
is in seconds
minimum_time_passed_between_shots = 1 / input_firerate
We calculate how many ticks should pass for the next shot.
minimum_ticks_passed_between_shots = minimum_time_passed_between_shots * 30
Keep in mind, the game can only check if enough time passed each tick. So, we need to round up. (This is the reason you fire faster at higher tick rates/fps. It is because your polling time for being allowed to shoot again is faster.)
effective_seconds_between_shots = minimum_ticks_passed_between_shots_rounded_up * 30
.
Btw, here is a cheat sheet that can be used to figure out if your fire rate number makes sense when modding CE (a game locked at 30 ticks per second) It has two tabs, check both out :) https://docs.google.com/spreadsheets/d/1bsZgmstxeR0Dkpi-iAuWN1nnNfAbiuL0SFJ06GpNwr4/edit#gid=2036459869
Programmatical:
import math
ORIG_TICKS_PER_SECOND = 30
effective_fire_rate = ORIG_TICKS_PER_SECOND / math.ceil(1 / input_firerate * ORIG_TICKS_PER_SECOND)
#include <math.h>
const float ORIG_TICKS_PER_SECOND = 30.0;
float effective_fire_rate = ORIG_TICKS_PER_SECOND / ceil(1 / input_firerate * ORIG_TICKS_PER_SECOND);
Challenges. Floating point precission is finnicky. The number might be just too high and an extra tick of waiting time gets added if you're running 30fps. So, it helps to add something like this:
const float SAFETY_VALUE = 30.0/1000.0/100.0 * 30.0; // Hundredth of a tick
// We add the safety value so the number of ticks between shots is definitely the one we want.
float effective_fire_rate = ORIG_TICKS_PER_SECOND / ceil(1 / input_firerate * ORIG_TICKS_PER_SECOND) + SAFETY_VALUE;
Here is me quickly proving the SAFETY_VALUE's method's integrity in a Python terminal:
>>> import math
>>> ORIG_TICKS_PER_SECOND = 30
>>> input_firerate = 14
>>> SAFETY_VALUE = 30/1000/100
>>> SAFETY_VALUE
0.0003
>>> 0.0003*30
0.009
>>> ORIG_TICKS_PER_SECOND / math.ceil(1 / input_firerate * ORIG_TICKS_PER_SECOND)
10.0
>>> effective_firerate = ORIG_TICKS_PER_SECOND / math.ceil(1 / input_firerate * ORIG_TICKS_PER_SECOND)
>>> effective_firerate
10.0
>>> effective_firerate += SAFETY_VALUE
>>> effective_firerate
10.009
>>> ORIG_TICKS_PER_SECOND / math.ceil(1 / effective_firerate * ORIG_TICKS_PER_SECOND)
10.0
The value comes out the same as in the effective calculation. THIS DOES MEAN IT FIRES SLIGHTLY QUICKER AT REALLY HIGH TICKRATES. BUT THE DIFFERENCE WILL BE LESS. THIS IS TO AVOID TOO SLOW SHOOTING AT 30FPS.
I ENCOURAGE THE SAFETY VALUE TO BE LOWER THAN IN THIS DEMONSTRATION. BUT NOT SO LOW THAT IT GETS LOST IN ROUNDING.