Skip to content
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

Fixed Point Math #3115

Merged
merged 1 commit into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion include/battle_ai_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 9 additions & 4 deletions include/battle_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 changes: 61 additions & 0 deletions include/fpmath.h
Original file line number Diff line number Diff line change
@@ -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_
27 changes: 1 addition & 26 deletions include/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))

Expand Down
12 changes: 6 additions & 6 deletions src/battle_ai_switch_items.c
Original file line number Diff line number Diff line change
Expand Up @@ -822,29 +822,29 @@ 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++)
{
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)
{
Expand Down
11 changes: 6 additions & 5 deletions src/battle_ai_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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);
Expand All @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/battle_dome.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading