- # The formula is originally: (3 max - 2 curr) rate bonus / (3 max)
- # I have reduced this to: (1 - 2/3 * pct) rate bonus
- # My rationale is that this cannot possibly be integer math, so rounding is
- # not a problem and commutation won't make a difference. It also
- # simplifies the input considerably.
- base_chance = (1 - 2/3 * percent_hp) * capture_rate \
- * ball_bonus * status_bonus
-
- shake_index = (base_chance / 255) ** 0.25 * (2**16 - 1)
+ # The actual formula uses (3 * max_hp - 2 * curr_hp) / (3 * max_hp)
+ # This uses (1 - 2/3 * curr_hp/max_hp)
+ # Integer division is taken into account by flooring immediately
+ # afterwards, so there should be no appreciable rounding error.
+ base_chance = int(
+ capture_rate * ball_bonus // 10 * (1 - 2/3 * percent_hp)
+ )
+ base_chance = base_chance * status_bonus // 10
+
+ # Shake index involves integer sqrt. Lovely.
+ isqrt = lambda x: int(x ** 0.5)
+ if not base_chance:
+ # This is very silly. Due to what must be an oversight, it's possible
+ # for the above formula to end with a zero chance to catch, which is
+ # then thrown blindly into the below denominator. Luckily, the games'
+ # division function is a no-op with a denominator of zero.. which
+ # means a base_chance of 0 is effectively a base chance of 1.
+ base_chance = 1
+ shake_index = 1048560 // isqrt(isqrt(16711680 // base_chance))