Fixed Point Math (#3115)

This commit is contained in:
Martin Griffin 2023-07-10 08:17:28 +01:00 committed by GitHub
commit d8c8ee1183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 279 additions and 229 deletions

View File

@ -656,7 +656,7 @@ struct BattleStruct
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.
u16 supremeOverlordModifier[MAX_BATTLERS_COUNT];
uq4_12_t supremeOverlordModifier[MAX_BATTLERS_COUNT];
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
bool8 trainerSlideHalfHpMsgDone;

View File

@ -87,7 +87,7 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split);
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness, bool32 considerZPower);
u8 GetMoveDamageResult(u16 move);
u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef);
u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
u16 *GetMovesArray(u32 battler);
bool32 IsConfusionMoveEffect(u16 moveEffect);

View File

@ -166,10 +166,10 @@ bool32 IsBattlerAlive(u8 battlerId);
u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move);
u32 GetBattlerWeight(u8 battlerId);
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier);
u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities);
u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
u16 GetTypeModifier(u8 atkType, u8 defType);
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, uq4_12_t *typeEffectivenessModifier);
uq4_12_t CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities);
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
uq4_12_t GetTypeModifier(u8 atkType, u8 defType);
s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId);
s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp);
bool32 CanMegaEvolve(u8 battlerId);
@ -240,4 +240,9 @@ u8 GetBattlerGender(u8 battlerId);
bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2);
u32 CalcSecondaryEffectChance(u8 battlerId, u8 secondaryEffectChance);
static inline u32 ApplyModifier(uq4_12_t modifier, u32 val)
{
return UQ_4_12_TO_INT((modifier * val) + UQ_4_12_ROUND);
}
#endif // GUARD_BATTLE_UTIL_H

61
include/fpmath.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef FPMATH_H_
#define FPMATH_H_
typedef s32 q4_12_t;
typedef u32 uq4_12_t;
#define Q_4_12_SHIFT (12)
#define UQ_4_12_SHIFT (12)
// Converts a number to Q8.8 fixed-point format
#define Q_8_8(n) ((s16)((n) * 256))
// Converts a number to Q4.12 fixed-point format
#define Q_4_12(n) ((q4_12_t)((n) * 4096))
#define UQ_4_12(n) ((uq4_12_t)((n) * 4096))
// Converts a number to Q24.8 fixed-point format
#define Q_24_8(n) ((s32)((n) * 256))
// Converts a Q8.8 fixed-point format number to a regular integer
#define Q_8_8_TO_INT(n) ((s32)((n) / 256))
// Converts a Q4.12 fixed-point format number to a regular integer
#define Q_4_12_TO_INT(n) ((s32)((n) / 4096))
#define UQ_4_12_TO_INT(n) ((u32)((n) / 4096))
// Converts a Q24.8 fixed-point format number to a regular integer
#define Q_24_8_TO_INT(n) ((s32)((n) / 256))
// Rounding value for Q4.12 fixed-point format
#define Q_4_12_ROUND ((1) << (Q_4_12_SHIFT - 1))
#define UQ_4_12_ROUND ((1) << (UQ_4_12_SHIFT - 1))
// Basic arithmetic for fixed point number formats
// Consumers should use encapsulated functions where possible
// FP API does not provide sanity checks against overflows
static inline uq4_12_t uq4_12_add(uq4_12_t a, uq4_12_t b)
{
return a + b;
}
static inline uq4_12_t uq4_12_subtract(uq4_12_t a, uq4_12_t b)
{
return a - b;
}
static inline uq4_12_t uq4_12_multiply(uq4_12_t a, uq4_12_t b)
{
u32 product = (u32) a * b;
return (product + UQ_4_12_ROUND) >> UQ_4_12_SHIFT;
}
static inline uq4_12_t uq4_12_divide(uq4_12_t dividend, uq4_12_t divisor)
{
if (divisor == UQ_4_12(0.0)) return UQ_4_12(0);
return (dividend << UQ_4_12_SHIFT) / divisor;
}
#endif // FPMATH_H_

View File

@ -5,6 +5,7 @@
#include <limits.h>
#include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "gba/gba.h"
#include "fpmath.h"
#include "constants/global.h"
#include "constants/flags.h"
#include "constants/vars.h"
@ -51,32 +52,6 @@
b = temp; \
}
// useful math macros
// Converts a number to Q8.8 fixed-point format
#define Q_8_8(n) ((s16)((n) * 256))
// Converts a number to Q4.12 fixed-point format
#define Q_4_12(n) ((s16)((n) * 4096))
#define UQ_4_12(n) ((u16)((n) * 4096))
// Converts a number to Q24.8 fixed-point format
#define Q_24_8(n) ((s32)((n) << 8))
// Converts a Q8.8 fixed-point format number to a regular integer
#define Q_8_8_TO_INT(n) ((int)((n) / 256))
// Converts a Q4.12 fixed-point format number to a regular integer
#define Q_4_12_TO_INT(n) ((int)((n) / 4096))
#define UQ_4_12_TO_INT(n) ((int)((n) / 4096))
// Converts a Q24.8 fixed-point format number to a regular integer
#define Q_24_8_TO_INT(n) ((int)((n) >> 8))
// Rounding value for Q4.12 fixed-point format
#define Q_4_12_ROUND ((1) << (12 - 1))
#define UQ_4_12_ROUND ((1) << (12 - 1))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) >= (b) ? (a) : (b))

View File

@ -822,7 +822,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
while (bits != 0x3F) // All mons were checked.
{
u16 bestResist = UQ_4_12(1.0);
uq4_12_t bestResist = UQ_4_12(1.0);
int bestMonId = PARTY_SIZE;
// Find the mon whose type is the most suitable defensively.
for (i = firstId; i < lastId; i++)
@ -830,21 +830,21 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
{
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
u16 typeEffectiveness = UQ_4_12(1.0);
uq4_12_t typeEffectiveness = UQ_4_12(1.0);
u8 atkType1 = gBattleMons[opposingBattler].type1;
u8 atkType2 = gBattleMons[opposingBattler].type2;
u8 defType1 = gSpeciesInfo[species].types[0];
u8 defType2 = gSpeciesInfo[species].types[1];
MulModifier(&typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
if (atkType2 != atkType1)
MulModifier(&typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
if (defType2 != defType1)
{
MulModifier(&typeEffectiveness, (GetTypeModifier(atkType1, defType2)));
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType2)));
if (atkType2 != atkType1)
MulModifier(&typeEffectiveness, (GetTypeModifier(atkType2, defType2)));
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType2)));
}
if (typeEffectiveness < bestResist)
{

View File

@ -31,7 +31,7 @@
} \
return FALSE
static u32 AI_GetEffectiveness(u16 multiplier);
static u32 AI_GetEffectiveness(uq4_12_t multiplier);
// Const Data
static const s8 sAiAbilityRatings[ABILITIES_COUNT] =
@ -768,7 +768,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness,
{
s32 dmg, moveType, critDmg, normalDmg;
s8 critChance;
u16 effectivenessMultiplier;
uq4_12_t effectivenessMultiplier;
if (considerZPower && IsViableZMove(battlerAtk, move))
{
@ -1003,9 +1003,10 @@ u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef)
return (bestDmg * 100) / gBattleMons[battlerDef].maxHP;
}
u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
{
u16 typeEffectiveness, moveType;
uq4_12_t typeEffectiveness;
u16 moveType;
SaveBattlerData(battlerAtk);
SaveBattlerData(battlerDef);
@ -1030,7 +1031,7 @@ u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef));
}
static u32 AI_GetEffectiveness(u16 multiplier)
static u32 AI_GetEffectiveness(uq4_12_t multiplier)
{
switch (multiplier)
{

View File

@ -5206,7 +5206,7 @@ static u16 GetWinningMove(int winnerTournamentId, int loserTournamentId, u8 roun
u32 personality = 0;
u32 targetSpecies = 0;
u32 targetAbility = 0;
u32 typeMultiplier = 0;
uq4_12_t typeMultiplier = 0;
do
{
personality = Random32();

File diff suppressed because it is too large Load Diff

18
test/fpmath.c Normal file
View File

@ -0,0 +1,18 @@
#include "global.h"
#include "test.h"
TEST("uq4_12_add adds 4.12 numbers") {
EXPECT_EQ(uq4_12_add(UQ_4_12(3.5), UQ_4_12(2.5)), UQ_4_12(6.0));
}
TEST("uq4_12_subtract subtracts 4.12 numbers") {
EXPECT_EQ(uq4_12_subtract(UQ_4_12(3.5), UQ_4_12(2.0)), UQ_4_12(1.5));
}
TEST("uq4_12_multiply multiplies 4.12 numbers") {
EXPECT_EQ(uq4_12_multiply(UQ_4_12(3.5), UQ_4_12(2.0)), UQ_4_12(7.0));
}
TEST("uq4_12_divide divides 4.12 numbers") {
EXPECT_EQ(uq4_12_divide(UQ_4_12(5.0), UQ_4_12(2.0)), UQ_4_12(2.5));
}