mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-03-21 18:04:50 -05:00
Use assertf to detect errors
This commit is contained in:
parent
cc8c8bd668
commit
35255475cb
Binary file not shown.
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 282 B |
|
|
@ -233,8 +233,24 @@ static inline bool8 IsPartnerTrainerId(u16 trainerId)
|
|||
|
||||
static inline u16 SanitizeTrainerId(u16 trainerId)
|
||||
{
|
||||
if (trainerId >= TRAINERS_COUNT && !IsPartnerTrainerId(trainerId))
|
||||
switch (trainerId)
|
||||
{
|
||||
case TRAINER_RECORD_MIXING_FRIEND:
|
||||
case TRAINER_RECORD_MIXING_APPRENTICE:
|
||||
case TRAINER_EREADER:
|
||||
case TRAINER_FRONTIER_BRAIN:
|
||||
case TRAINER_PLAYER:
|
||||
case TRAINER_SECRET_BASE:
|
||||
case TRAINER_LINK_OPPONENT:
|
||||
case TRAINER_UNION_ROOM:
|
||||
return TRAINER_NONE;
|
||||
}
|
||||
|
||||
assertf(trainerId < TRAINERS_COUNT || IsPartnerTrainerId(trainerId), "invalid trainer: %d", trainerId)
|
||||
{
|
||||
return TRAINER_NONE;
|
||||
}
|
||||
|
||||
return trainerId;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,7 +138,10 @@ struct MoveInfo
|
|||
union {
|
||||
struct {
|
||||
u16 stringId;
|
||||
u16 status;
|
||||
union {
|
||||
u16 status;
|
||||
u16 weather;
|
||||
};
|
||||
} twoTurnAttack;
|
||||
struct {
|
||||
u16 species;
|
||||
|
|
@ -175,10 +178,12 @@ extern const struct BattleMoveEffect gBattleMoveEffects[];
|
|||
|
||||
static inline u32 SanitizeMoveId(u32 moveId)
|
||||
{
|
||||
if (moveId >= MOVES_COUNT_ALL)
|
||||
assertf(moveId < MOVES_COUNT_ALL, "invalid move: %d", moveId)
|
||||
{
|
||||
return MOVE_NONE;
|
||||
else
|
||||
return moveId;
|
||||
}
|
||||
|
||||
return moveId;
|
||||
}
|
||||
|
||||
static inline const u8 *GetMoveName(u32 moveId)
|
||||
|
|
@ -231,12 +236,16 @@ static inline u32 GetMovePP(u32 moveId)
|
|||
|
||||
static inline u32 GetMoveZEffect(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].zMove.effect;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
assertf(GetMoveCategory(moveId) == DAMAGE_CATEGORY_STATUS, "not a status move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].zMove.effect;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveZPowerOverride(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].zMove.powerOverride;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
assertf(GetMoveCategory(moveId) != DAMAGE_CATEGORY_STATUS, "not a damaging move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].zMove.powerOverride;
|
||||
}
|
||||
|
||||
static inline s32 GetMovePriority(u32 moveId)
|
||||
|
|
@ -496,17 +505,26 @@ static inline bool32 IsValidApprenticeMove(u32 moveId)
|
|||
|
||||
static inline u32 GetMoveTwoTurnAttackStringId(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.stringId;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(gBattleMoveEffects[effect].twoTurnEffect, "not a two-turn move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.twoTurnAttack.stringId;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveTwoTurnAttackStatus(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.status;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_SEMI_INVULNERABLE || effect == EFFECT_SKY_DROP, "not a two-turn move with status: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.twoTurnAttack.status;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveTwoTurnAttackWeather(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.status;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_TWO_TURNS_ATTACK || effect == EFFECT_SOLAR_BEAM, "not a two-turn move with weather: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.twoTurnAttack.weather;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveSpeciesPowerOverride_Species(u32 moveId)
|
||||
|
|
@ -526,42 +544,60 @@ static inline u32 GetMoveSpeciesPowerOverride_NumOfHits(u32 moveId)
|
|||
|
||||
static inline enum ProtectMethod GetMoveProtectMethod(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.protectMethod;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveTerrainFlag(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.moveProperty;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_PROTECT || effect == EFFECT_ENDURE, "not a protect move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.protectMethod;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveEffectArg_Status(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.status;
|
||||
// Forward-declared here because 'include/battle_util.h' includes
|
||||
// this file.
|
||||
extern bool32 MoveHasAdditionalEffect(u32 move, u32 moveEffect);
|
||||
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_FOCUS_ENERGY || effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS || MoveHasAdditionalEffect(moveId, MOVE_EFFECT_REMOVE_STATUS), "not a move with status: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.status;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveEffectArg_MoveProperty(u32 moveId)
|
||||
{
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_FIRST_TURN_ONLY || effect == EFFECT_HEAL_PULSE, "not a move with moveProperty: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.moveProperty;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveEffectArg_HoldEffect(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.holdEffect;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_CHANGE_TYPE_ON_ITEM, "not a move with a hold effect: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.holdEffect;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveArgType(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.type;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_SOAK || effect == EFFECT_TWO_TYPED_MOVE || effect == EFFECT_THIRD_TYPE || effect == EFFECT_SUPER_EFFECTIVE_ON_ARG || effect == EFFECT_FAIL_IF_NOT_ARG_TYPE, "not a move with a type: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.type;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveFixedHPDamage(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.fixedDamage;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
assertf(gMovesInfo[moveId].effect == EFFECT_FIXED_HP_DAMAGE, "not a fixed-damage move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.fixedDamage;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveAbsorbPercentage(u32 moveId)
|
||||
{
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||
assertf(effect == EFFECT_ABSORB || effect == EFFECT_DREAM_EATER, "not an absorbing move: %S", gMovesInfo[moveId].name);
|
||||
if (gMovesInfo[moveId].argument.absorbPercentage == 0)
|
||||
return 50;
|
||||
return gMovesInfo[moveId].argument.absorbPercentage;
|
||||
|
|
@ -569,13 +605,15 @@ static inline u32 GetMoveAbsorbPercentage(u32 moveId)
|
|||
|
||||
static inline u32 GetMoveRecoil(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.recoilPercentage;
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
assertf(gMovesInfo[moveId].effect == EFFECT_RECOIL, "not a recoil move: %S", gMovesInfo[moveId].name);
|
||||
return gMovesInfo[moveId].argument.recoilPercentage;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveNonVolatileStatus(u32 move)
|
||||
{
|
||||
move = SanitizeMoveId(move);
|
||||
switch(GetMoveEffect(move))
|
||||
switch (GetMoveEffect(move))
|
||||
{
|
||||
case EFFECT_NON_VOLATILE_STATUS:
|
||||
case EFFECT_YAWN:
|
||||
|
|
@ -588,12 +626,16 @@ static inline u32 GetMoveNonVolatileStatus(u32 move)
|
|||
|
||||
static inline u32 GetMoveDamagePercentage(u32 move)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(move)].argument.damagePercentage;
|
||||
move = SanitizeMoveId(move);
|
||||
assertf(gMovesInfo[move].effect == EFFECT_FIXED_PERCENT_DAMAGE, "not a percentage-damage move: %S", gMovesInfo[move].name);
|
||||
return gMovesInfo[move].argument.damagePercentage;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveOverwriteAbility(u32 move)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(move)].argument.overwriteAbility;
|
||||
move = SanitizeMoveId(move);
|
||||
assertf(gMovesInfo[move].effect == EFFECT_OVERWRITE_ABILITY, "not a move that overwrites abilities: %S", gMovesInfo[move].name);
|
||||
return gMovesInfo[move].argument.overwriteAbility;
|
||||
}
|
||||
|
||||
static inline const struct AdditionalEffect *GetMoveAdditionalEffectById(u32 moveId, u32 effect)
|
||||
|
|
@ -624,9 +666,8 @@ static inline u32 GetMoveContestComboMoves(u32 moveId, u32 comboMove)
|
|||
static inline const u8 *GetMoveAnimationScript(u32 moveId)
|
||||
{
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
if (gMovesInfo[moveId].battleAnimScript == NULL)
|
||||
assertf(gMovesInfo[moveId].battleAnimScript, "No animation for %S", gMovesInfo[moveId].name)
|
||||
{
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "No animation for moveId=%u", moveId);
|
||||
return gMovesInfo[MOVE_NONE].battleAnimScript;
|
||||
}
|
||||
return gMovesInfo[moveId].battleAnimScript;
|
||||
|
|
@ -635,9 +676,8 @@ static inline const u8 *GetMoveAnimationScript(u32 moveId)
|
|||
static inline const u8 *GetMoveBattleScript(u32 moveId)
|
||||
{
|
||||
moveId = SanitizeMoveId(moveId);
|
||||
if (gBattleMoveEffects[GetMoveEffect(moveId)].battleScript == NULL)
|
||||
assertf(gBattleMoveEffects[GetMoveEffect(moveId)].battleScript, "No battle script for %S", gMovesInfo[moveId].name)
|
||||
{
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "No effect for moveId=%u", moveId);
|
||||
return gBattleMoveEffects[EFFECT_PLACEHOLDER].battleScript;
|
||||
}
|
||||
return gBattleMoveEffects[GetMoveEffect(moveId)].battleScript;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex);
|
|||
void TestRunner_Battle_CheckAiMoveScores(u32 battlerId);
|
||||
void TestRunner_Battle_AISetScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score);
|
||||
void TestRunner_Battle_AIAdjustScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score);
|
||||
void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex);
|
||||
void TestRunner_CheckMemory(void);
|
||||
|
||||
void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType);
|
||||
|
|
@ -48,7 +47,6 @@ u32 TestRunner_Battle_GetForcedEnvironment(void);
|
|||
#define TestRunner_Battle_CheckAiMoveScores(...) (void)0
|
||||
#define TestRunner_Battle_AISetScore(...) (void)0
|
||||
#define TestRunner_Battle_AIAdjustScore(...) (void)0
|
||||
#define TestRunner_Battle_InvalidNoHPMon(...) (void)0
|
||||
|
||||
#define TestRunner_Battle_CheckBattleRecordActionType(...) (void)0
|
||||
|
||||
|
|
|
|||
|
|
@ -880,9 +880,7 @@ static void Cmd_end(void)
|
|||
|
||||
if (!continuousAnim) // May have been used for debug?
|
||||
{
|
||||
// Debugging - ensure no hanging mon bg tasks
|
||||
if (FuncIsActiveTask(Task_UpdateMonBg))
|
||||
DebugPrintf("Move %d animation still has Task_UpdateMonBg active at the end!", gAnimMoveIndex);
|
||||
assertf(!FuncIsActiveTask(Task_UpdateMonBg), "move %d still has Task_UpdateMonBg active at the end", gAnimMoveIndex);
|
||||
|
||||
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256);
|
||||
if (!IsContest())
|
||||
|
|
|
|||
|
|
@ -1961,8 +1961,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
|
|||
if (speciesInfo->abilities[abilityNum] == partyData[monIndex].ability)
|
||||
break;
|
||||
}
|
||||
if (abilityNum >= maxAbilityNum)
|
||||
abilityNum = 0;
|
||||
assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[partyData[monIndex].ability], speciesInfo->speciesName);
|
||||
}
|
||||
else if (B_TRAINER_MON_RANDOM_ABILITY)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -956,22 +956,11 @@ static const struct PickupItem sPickupTable[] =
|
|||
|
||||
static void ValidateSavedBattlerCounts(void)
|
||||
{
|
||||
if (gBattleStruct->savedAttackerCount > 0)
|
||||
{
|
||||
if (TESTING)
|
||||
{
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!", __FILE__, __LINE__);
|
||||
}
|
||||
else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
|
||||
}
|
||||
if (gBattleStruct->savedTargetCount > 0)
|
||||
{
|
||||
if (TESTING)
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!", __FILE__, __LINE__);
|
||||
else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
|
||||
}
|
||||
// More calls to SaveBattlerAttacker than RestoreBattlerAttacker.
|
||||
assertf(gBattleStruct->savedAttackerCount == 0, "savedAttackerCount is greater than 0");
|
||||
|
||||
// More calls to SaveBattlerTarget than RestoreBattlerTarget.
|
||||
assertf(gBattleStruct->savedTargetCount == 0, "savedTargetCount is greater than 0");
|
||||
}
|
||||
|
||||
static bool32 NoTargetPresent(u8 battler, u32 move)
|
||||
|
|
@ -1145,6 +1134,8 @@ static inline bool32 IsBattlerUsingBeakBlast(u32 battler)
|
|||
static void Cmd_attackcanceler(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
assertf(gBattlerAttacker < gBattlersCount, "invalid gBattlerAttacker: %d", gBattlerAttacker);
|
||||
assertf(gBattlerTarget < gBattlersCount, "invalid gBattlerTarget: %d", gBattlerTarget);
|
||||
|
||||
if (gBattleStruct->battlerState[gBattlerAttacker].usedEjectItem)
|
||||
{
|
||||
|
|
@ -5862,6 +5853,8 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
|
|||
static void Cmd_moveend(void)
|
||||
{
|
||||
CMD_ARGS(u8 endMode, u8 endState);
|
||||
assertf(gBattlerAttacker < gBattlersCount, "invalid gBattlerAttacker: %d", gBattlerAttacker);
|
||||
assertf(gBattlerTarget < gBattlersCount, "invalid gBattlerTarget: %d", gBattlerTarget);
|
||||
|
||||
s32 i;
|
||||
bool32 effect = FALSE;
|
||||
|
|
@ -6878,6 +6871,8 @@ static void Cmd_moveend(void)
|
|||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
|
||||
assertf(gBattlerAttacker < gBattlersCount, "invalid gBattlerAttacker: %d", gBattlerAttacker);
|
||||
assertf(gBattlerTarget < gBattlersCount, "invalid gBattlerTarget: %d", gBattlerTarget);
|
||||
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||||
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
||||
|
|
@ -7049,6 +7044,36 @@ static void Cmd_returnatktoball(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
static bool32 IsValidSwitchIn(enum BattleSide side, u32 index)
|
||||
{
|
||||
if (index >= PARTY_SIZE)
|
||||
return FALSE;
|
||||
|
||||
struct Pokemon *party = GetSideParty(side);
|
||||
if (!IsValidForBattle(&party[index]))
|
||||
return FALSE;
|
||||
|
||||
for (u32 i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (GetBattlerSide(i) == side && gBattlerPartyIndexes[i] == index && IsBattlerAlive(i))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static u32 GetArbitraryValidSwitchIn(enum BattleSide side)
|
||||
{
|
||||
for (u32 i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (IsValidSwitchIn(side, i))
|
||||
return i;
|
||||
}
|
||||
|
||||
assertf(FALSE, "no valid switch ins for side: %d", side);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Cmd_getswitchedmondata(void)
|
||||
{
|
||||
CMD_ARGS(u8 battler);
|
||||
|
|
@ -7057,10 +7082,11 @@ static void Cmd_getswitchedmondata(void)
|
|||
if (gBattleControllerExecFlags)
|
||||
return;
|
||||
|
||||
if (TESTING
|
||||
&& gBattlerPartyIndexes[battler] == gBattleStruct->monToSwitchIntoId[battler]
|
||||
&& IsBattlerAlive(battler))
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, 0, ":L:%s:%d: battler is trying to switch to themself", __FILE__, __LINE__);
|
||||
enum BattleSide side = GetBattlerSide(battler);
|
||||
assertf(IsValidSwitchIn(side, gBattleStruct->monToSwitchIntoId[battler]))
|
||||
{
|
||||
gBattleStruct->monToSwitchIntoId[battler] = GetArbitraryValidSwitchIn(side);
|
||||
}
|
||||
|
||||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
|
||||
|
||||
|
|
@ -7088,33 +7114,13 @@ static void Cmd_switchindataupdate(void)
|
|||
for (i = 0; i < sizeof(struct BattlePokemon); i++)
|
||||
monData[i] = gBattleResources->bufferB[battler][4 + i];
|
||||
|
||||
// Edge case: the sent out pokemon has 0 HP. This should never happen.
|
||||
if (!IsBattlerAlive(battler))
|
||||
enum BattleSide side = GetBattlerSide(battler);
|
||||
assertf(IsBattlerAlive(battler))
|
||||
{
|
||||
// If it's a test, mark it as invalid.
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
TestRunner_Battle_InvalidNoHPMon(battler, gBattlerPartyIndexes[battler]);
|
||||
}
|
||||
// Handle in-game scenario.
|
||||
else
|
||||
{
|
||||
struct Pokemon *party = GetBattlerParty(battler);
|
||||
// Find the first possible replacement for the not valid pokemon.
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (IsValidForBattle(&party[i]))
|
||||
break;
|
||||
}
|
||||
// There is valid replacement.
|
||||
if (i != PARTY_SIZE)
|
||||
{
|
||||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler] = i;
|
||||
BtlController_EmitGetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_ALL_BATTLE, 1u << gBattlerPartyIndexes[battler]);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler] = GetArbitraryValidSwitchIn(side);
|
||||
BtlController_EmitGetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_ALL_BATTLE, 1u << gBattlerPartyIndexes[battler]);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
return;
|
||||
}
|
||||
|
||||
gBattleMons[battler].types[0] = GetSpeciesType(gBattleMons[battler].species, 0);
|
||||
|
|
@ -9243,12 +9249,14 @@ static void Cmd_unused_0x78(void)
|
|||
static void TryResetProtectUseCounter(u32 battler)
|
||||
{
|
||||
u32 lastMove = gLastResultingMoves[battler];
|
||||
enum BattleMoveEffects lastEffect = GetMoveEffect(lastMove);
|
||||
if (lastMove == MOVE_UNAVAILABLE)
|
||||
{
|
||||
gBattleMons[battler].volatiles.protectUses = 0;
|
||||
return;
|
||||
}
|
||||
else if (!gBattleMoveEffects[lastEffect].usesProtectCounter)
|
||||
|
||||
enum BattleMoveEffects lastEffect = GetMoveEffect(lastMove);
|
||||
if (!gBattleMoveEffects[lastEffect].usesProtectCounter)
|
||||
{
|
||||
if (GetConfig(CONFIG_ALLY_SWITCH_FAIL_CHANCE) < GEN_9 || lastEffect != EFFECT_ALLY_SWITCH)
|
||||
gBattleMons[battler].volatiles.protectUses = 0;
|
||||
|
|
@ -13962,18 +13970,22 @@ static void Cmd_callnative(void)
|
|||
|
||||
void SaveBattlerTarget(u32 battler)
|
||||
{
|
||||
if (gBattleStruct->savedTargetCount < NELEMS(gBattleStruct->savedBattlerTarget))
|
||||
gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler;
|
||||
else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerTarget array size!");
|
||||
assertf(gBattleStruct->savedTargetCount < ARRAY_COUNT(gBattleStruct->savedBattlerTarget), "Too many savedBattlerTargets")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler;
|
||||
}
|
||||
|
||||
void SaveBattlerAttacker(u32 battler)
|
||||
{
|
||||
if (gBattleStruct->savedAttackerCount < NELEMS(gBattleStruct->savedBattlerAttacker))
|
||||
gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler;
|
||||
else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerAttacker array size!");
|
||||
assertf(gBattleStruct->savedAttackerCount < ARRAY_COUNT(gBattleStruct->savedBattlerAttacker), "Too many savedBattlerAttackers")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler;
|
||||
}
|
||||
|
||||
void BS_SaveTarget(void)
|
||||
|
|
@ -13986,19 +13998,15 @@ void BS_SaveTarget(void)
|
|||
void BS_RestoreTarget(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
if (gBattleStruct->savedTargetCount > 0)
|
||||
assertf(gBattleStruct->savedTargetCount > 0, "No savedBattlerTargets")
|
||||
{
|
||||
gBattleStruct->savedTargetCount--;
|
||||
gBattlerTarget = gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount];
|
||||
}
|
||||
else
|
||||
{
|
||||
// #if TESTING
|
||||
// Test_ExitWithResult(TEST_RESULT_ERROR, "BS_RestoreTarget attempting to restore an empty target!");
|
||||
// #else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "BS_RestoreTarget attempting to restore an empty target!");
|
||||
// #endif
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
gBattleStruct->savedTargetCount--;
|
||||
gBattlerTarget = gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount];
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
@ -14012,19 +14020,14 @@ void BS_SaveAttacker(void)
|
|||
void BS_RestoreAttacker(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
if (gBattleStruct->savedAttackerCount > 0)
|
||||
assertf(gBattleStruct->savedAttackerCount > 0, "No savedBattlerAttackers")
|
||||
{
|
||||
gBattleStruct->savedAttackerCount--;
|
||||
gBattlerAttacker = gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount];
|
||||
}
|
||||
else
|
||||
{
|
||||
// #if TESTING
|
||||
// Test_ExitWithResult(TEST_RESULT_ERROR, "BS_RestoreAttacker attempting to restore an empty attacker!");
|
||||
// #else
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "BS_RestoreAttacker attempting to restore an empty attacker!");
|
||||
// #endif
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
gBattleStruct->savedAttackerCount--;
|
||||
gBattlerAttacker = gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount];
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -282,6 +282,9 @@ bool32 EndOrContinueWeather(void)
|
|||
static u32 CalcBeatUpPower(void)
|
||||
{
|
||||
u32 species = gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++];
|
||||
// FIXME: Why call CalcBeatUpPower when 'beatUpSlot' is OOB?
|
||||
if (species == 0xFFFF)
|
||||
return 0;
|
||||
return (GetSpeciesBaseAttack(species) / 10) + 5;
|
||||
}
|
||||
|
||||
|
|
@ -10517,7 +10520,7 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect)
|
|||
|
||||
bool32 IsMoveEffectRemoveSpeciesType(u32 move, u32 moveEffect, u32 argument)
|
||||
{
|
||||
return (GetMoveArgType(move) == argument) && MoveHasAdditionalEffectSelf(move, moveEffect);
|
||||
return (MoveHasAdditionalEffectSelf(move, moveEffect) && GetMoveArgType(move) == argument);
|
||||
}
|
||||
|
||||
bool32 MoveHasChargeTurnAdditionalEffect(u32 move)
|
||||
|
|
|
|||
|
|
@ -5327,15 +5327,21 @@ static void Task_WaitForSliderHeartAnim(u8 taskId)
|
|||
|
||||
static u16 SanitizeMove(u16 move)
|
||||
{
|
||||
if (move >= MOVES_COUNT)
|
||||
move = MOVE_POUND;
|
||||
assertf(move < MOVES_COUNT, "invalid move: %d", move)
|
||||
{
|
||||
return MOVE_POUND;
|
||||
}
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
static u16 SanitizeSpecies(u16 species)
|
||||
{
|
||||
if (species >= NUM_SPECIES)
|
||||
species = SPECIES_NONE;
|
||||
assertf(species < NUM_SPECIES, "invalid species: %d", species)
|
||||
{
|
||||
return SPECIES_NONE;
|
||||
}
|
||||
|
||||
return species;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2118,7 +2118,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||
.sleepTalkBanned = TRUE,
|
||||
.instructBanned = TRUE,
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_PKMNTOOKSUNLIGHT, .status = B_WEATHER_SUN },
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_PKMNTOOKSUNLIGHT, .weather = B_WEATHER_SUN },
|
||||
.contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL : CONTEST_EFFECT_HIGHLY_APPEALING,
|
||||
.contestCategory = CONTEST_CATEGORY_COOL,
|
||||
.contestComboStarterId = 0,
|
||||
|
|
@ -3757,6 +3757,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.target = MOVE_TARGET_SELECTED,
|
||||
.priority = 0,
|
||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||
.argument = { .absorbPercentage = 50 },
|
||||
.healingMove = B_HEAL_BLOCKING >= GEN_6,
|
||||
.contestEffect = CONTEST_EFFECT_STARTLE_PREV_MONS,
|
||||
.contestCategory = CONTEST_CATEGORY_SMART,
|
||||
|
|
@ -5517,6 +5518,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.priority = 2,
|
||||
#endif
|
||||
.category = DAMAGE_CATEGORY_STATUS,
|
||||
.argument = { .protectMethod = PROTECT_NONE },
|
||||
.zMove = { .effect = Z_EFFECT_RESET_STATS },
|
||||
.ignoresProtect = TRUE,
|
||||
.mirrorMoveBanned = TRUE,
|
||||
|
|
@ -16277,7 +16279,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.slicingMove = TRUE,
|
||||
.sleepTalkBanned = TRUE,
|
||||
.instructBanned = TRUE,
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_PKMNTOOKSUNLIGHT, .status = B_WEATHER_SUN },
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_PKMNTOOKSUNLIGHT, .weather = B_WEATHER_SUN },
|
||||
.contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
|
||||
.contestCategory = CONTEST_CATEGORY_TOUGH,
|
||||
.contestComboStarterId = 0,
|
||||
|
|
@ -21026,7 +21028,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.target = MOVE_TARGET_SELECTED,
|
||||
.priority = 0,
|
||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_ELECTROSHOTCHARGING, .status = B_WEATHER_RAIN },
|
||||
.argument.twoTurnAttack = { .stringId = STRINGID_ELECTROSHOTCHARGING, .weather = B_WEATHER_RAIN },
|
||||
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||
.moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1,
|
||||
.self = TRUE,
|
||||
|
|
|
|||
|
|
@ -1,193 +1,9 @@
|
|||
#include "decompress_error_handler.h"
|
||||
#include "global.h"
|
||||
#include "data.h"
|
||||
#include "menu.h"
|
||||
#include "menu_helpers.h"
|
||||
#include "malloc.h"
|
||||
#include "palette.h"
|
||||
#include "graphics.h"
|
||||
#include "gpu_regs.h"
|
||||
#include "bg.h"
|
||||
#include "main.h"
|
||||
#include "text_window.h"
|
||||
#include "string_util.h"
|
||||
#include "constants/rgb.h"
|
||||
|
||||
static EWRAM_DATA u32 sErrorAddress;
|
||||
static EWRAM_DATA enum CompressionError sCompressionError;
|
||||
|
||||
static const struct BgTemplate sBgTemplates[3] =
|
||||
{
|
||||
{
|
||||
.bg = 0,
|
||||
.charBaseIndex = 2,
|
||||
.mapBaseIndex = 31,
|
||||
.screenSize = 0,
|
||||
.paletteMode = 0,
|
||||
.priority = 0,
|
||||
.baseTile = 0,
|
||||
},
|
||||
{
|
||||
.bg = 2,
|
||||
.charBaseIndex = 0,
|
||||
.mapBaseIndex = 14,
|
||||
.screenSize = 0,
|
||||
.paletteMode = 0,
|
||||
.priority = 2,
|
||||
.baseTile = 0,
|
||||
},
|
||||
{
|
||||
.bg = 3,
|
||||
.charBaseIndex = 0,
|
||||
.mapBaseIndex = 15,
|
||||
.screenSize = 0,
|
||||
.paletteMode = 0,
|
||||
.priority = 3,
|
||||
.baseTile = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static void DecompressErrorScreenTextPrint(u32 window, const u8 *text, u8 x, u8 y)
|
||||
{
|
||||
u8 color[3];
|
||||
|
||||
color[0] = TEXT_COLOR_TRANSPARENT;
|
||||
color[1] = TEXT_DYNAMIC_COLOR_6;
|
||||
color[2] = TEXT_COLOR_LIGHT_GRAY;
|
||||
AddTextPrinterParameterized4(window, FONT_NORMAL, x * 8, y * 8 + 1, 0, 0, color, 0, text);
|
||||
}
|
||||
|
||||
static void GetHexStringFromU32(u8 *str, u32 value)
|
||||
{
|
||||
str[0] = CHAR_0;
|
||||
str[1] = CHAR_x;
|
||||
str[10] = EOS;
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
u8 currChar = 0;
|
||||
switch ((value >> (4*i)) & 0xF)
|
||||
{
|
||||
case 0:
|
||||
currChar = CHAR_0;
|
||||
break;
|
||||
case 1:
|
||||
currChar = CHAR_1;
|
||||
break;
|
||||
case 2:
|
||||
currChar = CHAR_2;
|
||||
break;
|
||||
case 3:
|
||||
currChar = CHAR_3;
|
||||
break;
|
||||
case 4:
|
||||
currChar = CHAR_4;
|
||||
break;
|
||||
case 5:
|
||||
currChar = CHAR_5;
|
||||
break;
|
||||
case 6:
|
||||
currChar = CHAR_6;
|
||||
break;
|
||||
case 7:
|
||||
currChar = CHAR_7;
|
||||
break;
|
||||
case 8:
|
||||
currChar = CHAR_8;
|
||||
break;
|
||||
case 9:
|
||||
currChar = CHAR_9;
|
||||
break;
|
||||
case 10:
|
||||
currChar = CHAR_A;
|
||||
break;
|
||||
case 11:
|
||||
currChar = CHAR_B;
|
||||
break;
|
||||
case 12:
|
||||
currChar = CHAR_C;
|
||||
break;
|
||||
case 13:
|
||||
currChar = CHAR_D;
|
||||
break;
|
||||
case 14:
|
||||
currChar = CHAR_E;
|
||||
break;
|
||||
case 15:
|
||||
currChar = CHAR_F;
|
||||
break;
|
||||
}
|
||||
u32 pos = 9 - i;
|
||||
str[pos] = currChar;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct WindowTemplate sTextWin =
|
||||
{
|
||||
.bg = 0,
|
||||
.tilemapLeft = 3,
|
||||
.tilemapTop = 2,
|
||||
.width = 24,
|
||||
.height = 16,
|
||||
.paletteNum = 15,
|
||||
.baseBlock = 1,
|
||||
};
|
||||
|
||||
void DecompressionError_CB2(void)
|
||||
{
|
||||
|
||||
if (sErrorAddress == 0)
|
||||
return;
|
||||
|
||||
ResetVramOamAndBgCntRegs();
|
||||
ResetAllBgsCoordinates();
|
||||
FreeAllWindowBuffers();
|
||||
|
||||
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
||||
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
||||
SetGpuReg(REG_OFFSET_BG0CNT, 0);
|
||||
SetGpuReg(REG_OFFSET_BG0HOFS, 0);
|
||||
SetGpuReg(REG_OFFSET_BG0VOFS, 0);
|
||||
DmaFill16(3, 0, VRAM, VRAM_SIZE);
|
||||
DmaFill32(3, 0, OAM, OAM_SIZE);
|
||||
DmaFill16(3, 0, PLTT, PLTT_SIZE);
|
||||
ResetBgsAndClearDma3BusyFlags(0);
|
||||
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
|
||||
LoadBgTiles(0, gTextWindowFrame1_Gfx, 0x120, 0x214);
|
||||
DeactivateAllTextPrinters();
|
||||
ResetTasks();
|
||||
ResetPaletteFade();
|
||||
LoadPalette(gTextWindowFrame1_Pal, 0xE0, 0x20);
|
||||
LoadPalette(gStandardMenuPalette, 0xF0, 0x20);
|
||||
u32 window = AddWindow(&sTextWin);
|
||||
DrawStdFrameWithCustomTileAndPalette(window, TRUE, 0x214, 0xE);
|
||||
static const u8 romCheckFailMessage[] =_(
|
||||
"{COLOR RED}ERROR! {COLOR DARK_GRAY}Decompression Failed!\n"
|
||||
"\n"
|
||||
"Address:\n"
|
||||
"Error:\n");
|
||||
DecompressErrorScreenTextPrint(window, romCheckFailMessage, 1, 0);
|
||||
u8 addressStr[11];
|
||||
u8 errorStr[11];
|
||||
GetHexStringFromU32(addressStr, sErrorAddress);
|
||||
GetHexStringFromU32(errorStr, sCompressionError);
|
||||
DecompressErrorScreenTextPrint(window, addressStr, 7, 4);
|
||||
DecompressErrorScreenTextPrint(window, errorStr, 7, 6);
|
||||
TransferPlttBuffer();
|
||||
*(u16*)PLTT = RGB(17, 18, 31);
|
||||
ShowBg(0);
|
||||
sErrorAddress = 0;
|
||||
// This loop is apparently needed to prevent the game from doing
|
||||
// stupid stuff with data it couldn't decompress
|
||||
while(TRUE)
|
||||
sCompressionError++;
|
||||
}
|
||||
#include "decompress_error_handler.h"
|
||||
|
||||
void DecompressionError(const u32 *src, enum CompressionError error)
|
||||
{
|
||||
sErrorAddress = (u32)src;
|
||||
sCompressionError = error;
|
||||
SetMainCallback2(DecompressionError_CB2);
|
||||
DecompressionError_CB2();
|
||||
assertf(0, "Decompression failed.\nAddress: 0x%p\nError: 0x%x\n", src, error);
|
||||
}
|
||||
|
||||
void DoDecompressionError(void)
|
||||
|
|
|
|||
|
|
@ -2314,12 +2314,13 @@ static void DexNavGuiInit(MainCallback callback)
|
|||
|
||||
void Task_OpenDexNavFromStartMenu(u8 taskId)
|
||||
{
|
||||
if (DEXNAV_ENABLED == FALSE)
|
||||
{ // must have it enabled to enter
|
||||
DebugPrintfLevel(MGBA_LOG_ERROR, "DexNav was opened when DEXNAV_ENABLED config was disabled! Check include/config/dexnav.h");
|
||||
assertf(DEXNAV_ENABLED, "DexNav was opened when DEXNAV_ENABLED config was disabled! Check include/config/dexnav.h")
|
||||
{
|
||||
DestroyTask(taskId);
|
||||
return;
|
||||
}
|
||||
else if (!gPaletteFade.active)
|
||||
|
||||
if (!gPaletteFade.active)
|
||||
{
|
||||
CleanupOverworldWindowsAndTilemaps();
|
||||
DexNavGuiInit(CB2_ReturnToFieldWithOpenMenu);
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ static void SetPositionFromConnection(const struct MapConnection *connection, in
|
|||
gSaveBlock1Ptr->pos.y = mapHeader->mapLayout->height;
|
||||
break;
|
||||
default:
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "SetPositionFromConnection was passed an invalid direction (%d)!", direction);
|
||||
assertf(0, "invalid direction: %d", direction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -664,23 +664,21 @@ bool8 CameraMove(int x, int y)
|
|||
old_x = gSaveBlock1Ptr->pos.x;
|
||||
old_y = gSaveBlock1Ptr->pos.y;
|
||||
connection = GetIncomingConnection(direction, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
|
||||
if (connection)
|
||||
assertf(connection)
|
||||
{
|
||||
SetPositionFromConnection(connection, direction, x, y);
|
||||
LoadMapFromCameraTransition(connection->mapGroup, connection->mapNum);
|
||||
gCamera.active = TRUE;
|
||||
gCamera.x = old_x - gSaveBlock1Ptr->pos.x;
|
||||
gCamera.y = old_y - gSaveBlock1Ptr->pos.y;
|
||||
gSaveBlock1Ptr->pos.x += x;
|
||||
gSaveBlock1Ptr->pos.y += y;
|
||||
MoveMapViewToBackup(direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "GetIncomingConnection returned an invalid connection inside CameraMove!");
|
||||
return gCamera.active;
|
||||
}
|
||||
|
||||
SetPositionFromConnection(connection, direction, x, y);
|
||||
LoadMapFromCameraTransition(connection->mapGroup, connection->mapNum);
|
||||
gCamera.active = TRUE;
|
||||
gCamera.x = old_x - gSaveBlock1Ptr->pos.x;
|
||||
gCamera.y = old_y - gSaveBlock1Ptr->pos.y;
|
||||
gSaveBlock1Ptr->pos.x += x;
|
||||
gSaveBlock1Ptr->pos.y += y;
|
||||
MoveMapViewToBackup(direction);
|
||||
}
|
||||
|
||||
return gCamera.active;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -792,10 +792,12 @@ bool32 RemovePyramidBagItem(u16 itemId, u16 count)
|
|||
|
||||
static u16 SanitizeItemId(u16 itemId)
|
||||
{
|
||||
if (itemId >= ITEMS_COUNT)
|
||||
assertf(itemId < ITEMS_COUNT, "invalid item: %d", itemId)
|
||||
{
|
||||
return ITEM_NONE;
|
||||
else
|
||||
return itemId;
|
||||
}
|
||||
|
||||
return itemId;
|
||||
}
|
||||
|
||||
const u8 *GetItemName(u16 itemId)
|
||||
|
|
|
|||
11
src/malloc.c
11
src/malloc.c
|
|
@ -81,9 +81,9 @@ void *AllocInternal(void *heapStart, u32 size, const char *location)
|
|||
}
|
||||
}
|
||||
|
||||
#if TESTING
|
||||
if (pos->next == head)
|
||||
{
|
||||
#if TESTING
|
||||
const struct MemBlock *head = HeapHead();
|
||||
const struct MemBlock *block = head;
|
||||
do
|
||||
|
|
@ -99,13 +99,10 @@ void *AllocInternal(void *heapStart, u32 size, const char *location)
|
|||
block = block->next;
|
||||
}
|
||||
while (block != head);
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":L%s:%d, %s: OOM allocating %d bytes", gTestRunnerState.test->filename, SourceLine(0), location, size);
|
||||
}
|
||||
#endif
|
||||
if (location)
|
||||
{
|
||||
DebugPrintfLevel(MGBA_LOG_ERROR, "%s: out of memory trying to allocate %d bytes", location, size);
|
||||
}
|
||||
AGB_ASSERT(FALSE);
|
||||
assertf(pos->next != head, "%s: out of memory trying to allocate %d bytes", location, size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -534,37 +534,33 @@ void LoadSaveblockObjEventScripts(void)
|
|||
savObjTemplates[i].script = mapHeaderObjTemplates[i].script;
|
||||
}
|
||||
|
||||
static struct ObjectEventTemplate *GetObjectEventTemplate(u8 localId)
|
||||
{
|
||||
for (u32 i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
||||
{
|
||||
if (gSaveBlock1Ptr->objectEventTemplates[i].localId == localId)
|
||||
return &gSaveBlock1Ptr->objectEventTemplates[i];
|
||||
}
|
||||
|
||||
assertf(0, "no object event template for localId %d", localId);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SetObjEventTemplateCoords(u8 localId, s16 x, s16 y)
|
||||
{
|
||||
s32 i;
|
||||
struct ObjectEventTemplate *savObjTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
||||
|
||||
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
||||
struct ObjectEventTemplate *objectEventTemplate = GetObjectEventTemplate(localId);
|
||||
if (objectEventTemplate)
|
||||
{
|
||||
struct ObjectEventTemplate *objectEventTemplate = &savObjTemplates[i];
|
||||
if (objectEventTemplate->localId == localId)
|
||||
{
|
||||
objectEventTemplate->x = x;
|
||||
objectEventTemplate->y = y;
|
||||
return;
|
||||
}
|
||||
objectEventTemplate->x = x;
|
||||
objectEventTemplate->y = y;
|
||||
}
|
||||
}
|
||||
|
||||
void SetObjEventTemplateMovementType(u8 localId, u8 movementType)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
struct ObjectEventTemplate *savObjTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
||||
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
||||
{
|
||||
struct ObjectEventTemplate *objectEventTemplate = &savObjTemplates[i];
|
||||
if (objectEventTemplate->localId == localId)
|
||||
{
|
||||
objectEventTemplate->movementType = movementType;
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct ObjectEventTemplate *objectEventTemplate = GetObjectEventTemplate(localId);
|
||||
if (objectEventTemplate)
|
||||
objectEventTemplate->movementType = movementType;
|
||||
}
|
||||
|
||||
static void InitMapView(void)
|
||||
|
|
|
|||
|
|
@ -1227,31 +1227,66 @@ void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV,
|
|||
CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0);
|
||||
}
|
||||
|
||||
static bool32 GenderRatioCanBe(u32 genderRatio, u32 gender)
|
||||
{
|
||||
switch (gender)
|
||||
{
|
||||
case MON_MALE:
|
||||
return genderRatio != MON_FEMALE && genderRatio != MON_GENDERLESS;
|
||||
case MON_FEMALE:
|
||||
return genderRatio != MON_MALE && genderRatio != MON_GENDERLESS;
|
||||
case MON_GENDERLESS:
|
||||
return genderRatio == MON_GENDERLESS;
|
||||
default:
|
||||
assertf(FALSE, "unknown gender: %d", gender);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter)
|
||||
{
|
||||
u32 personality;
|
||||
u32 genderRatio = gSpeciesInfo[species].genderRatio;
|
||||
|
||||
if ((u8)(unownLetter - 1) < NUM_UNOWN_FORMS)
|
||||
{
|
||||
u16 actualLetter;
|
||||
|
||||
do
|
||||
while (TRUE)
|
||||
{
|
||||
personality = Random32();
|
||||
actualLetter = GET_UNOWN_LETTER(personality);
|
||||
|
||||
assertf(GenderRatioCanBe(genderRatio, gender), "genderRatio %d can't be gender %d", genderRatio, gender)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (nature == GetNatureFromPersonality(personality)
|
||||
&& gender == GetGenderFromSpeciesAndPersonality(species, personality)
|
||||
&& actualLetter == unownLetter - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (nature != GetNatureFromPersonality(personality)
|
||||
|| gender != GetGenderFromSpeciesAndPersonality(species, personality)
|
||||
|| actualLetter != unownLetter - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
while (TRUE)
|
||||
{
|
||||
personality = Random32();
|
||||
|
||||
assertf(GenderRatioCanBe(genderRatio, gender), "genderRatio %d can't be gender %d", genderRatio, gender)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (nature == GetNatureFromPersonality(personality)
|
||||
&& gender == GetGenderFromSpeciesAndPersonality(species, personality))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (nature != GetNatureFromPersonality(personality)
|
||||
|| gender != GetGenderFromSpeciesAndPersonality(species, personality));
|
||||
}
|
||||
|
||||
CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0);
|
||||
|
|
@ -1262,13 +1297,22 @@ void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level)
|
|||
{
|
||||
u32 personality;
|
||||
u32 otId;
|
||||
u32 genderRatio = gSpeciesInfo[species].genderRatio;
|
||||
|
||||
do
|
||||
while (TRUE)
|
||||
{
|
||||
otId = Random32();
|
||||
personality = Random32();
|
||||
|
||||
assertf(GenderRatioCanBe(genderRatio, MON_MALE), "genderRatio %d can't be MON_MALE", genderRatio)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetGenderFromSpeciesAndPersonality(species, personality) == MON_MALE)
|
||||
break;
|
||||
}
|
||||
while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE);
|
||||
|
||||
CreateMon(mon, species, level, USE_RANDOM_IVS, TRUE, personality, OT_ID_PRESET, otId);
|
||||
}
|
||||
|
||||
|
|
@ -7188,10 +7232,12 @@ bool32 TryFormChange(u32 monId, enum BattleSide side, enum FormChanges method)
|
|||
|
||||
u16 SanitizeSpeciesId(u16 species)
|
||||
{
|
||||
if (species > NUM_SPECIES || !IsSpeciesEnabled(species))
|
||||
assertf(species <= NUM_SPECIES && (species == SPECIES_NONE || IsSpeciesEnabled(species)), "invalid species: %d", species)
|
||||
{
|
||||
return SPECIES_NONE;
|
||||
else
|
||||
return species;
|
||||
}
|
||||
|
||||
return species;
|
||||
}
|
||||
|
||||
bool32 IsSpeciesEnabled(u16 species)
|
||||
|
|
|
|||
|
|
@ -173,14 +173,17 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz
|
|||
|
||||
u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
|
||||
{
|
||||
assertf(lo <= hi);
|
||||
return lo + (((hi - lo + 1) * Random()) >> 16);
|
||||
}
|
||||
|
||||
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||
{
|
||||
assertf(lo <= hi);
|
||||
LOOP_RANDOM_START;
|
||||
while (TRUE)
|
||||
{
|
||||
// TODO: assertf to abort after too many iterations.
|
||||
u32 n = lo + (((hi - lo + 1) * LOOP_RANDOM) >> 16);
|
||||
if (!reject(n))
|
||||
return n;
|
||||
|
|
@ -190,6 +193,7 @@ u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reje
|
|||
|
||||
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||
{
|
||||
assertf(n > 0);
|
||||
s32 i, targetSum;
|
||||
targetSum = (sum * Random()) >> 16;
|
||||
for (i = 0; i < n - 1; i++)
|
||||
|
|
@ -203,6 +207,7 @@ u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *wei
|
|||
|
||||
const void *RandomElementArrayDefault(enum RandomTag tag, const void *array, size_t size, size_t count)
|
||||
{
|
||||
assertf(count > 0);
|
||||
return (const u8 *)array + size * RandomUniformDefault(tag, 0, count - 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,11 @@ void ScriptJump(struct ScriptContext *ctx, const u8 *ptr)
|
|||
|
||||
void ScriptCall(struct ScriptContext *ctx, const u8 *ptr)
|
||||
{
|
||||
ScriptPush(ctx, ctx->scriptPtr);
|
||||
bool32 failed = ScriptPush(ctx, ctx->scriptPtr);
|
||||
assertf(!failed, "could not push %p", ptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ctx->scriptPtr = ptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,9 +243,20 @@ void CanHyperTrain(struct ScriptContext *ctx)
|
|||
|
||||
Script_RequestEffects(SCREFF_V1);
|
||||
|
||||
if (stat < NUM_STATS
|
||||
&& partyIndex < PARTY_SIZE
|
||||
&& !GetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat)
|
||||
assertf(stat < NUM_STATS, "invalid stat: %d", stat)
|
||||
{
|
||||
gSpecialVar_Result = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
CalculatePlayerPartyCount();
|
||||
assertf(partyIndex < gPlayerPartyCount, "invalid party index: %d", partyIndex)
|
||||
{
|
||||
gSpecialVar_Result = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat)
|
||||
&& GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP_IV + stat) < MAX_PER_STAT_IVS)
|
||||
{
|
||||
gSpecialVar_Result = TRUE;
|
||||
|
|
@ -263,12 +274,20 @@ void HyperTrain(struct ScriptContext *ctx)
|
|||
|
||||
Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE);
|
||||
|
||||
if (stat < NUM_STATS && partyIndex < PARTY_SIZE)
|
||||
assertf(stat < NUM_STATS, "invalid stat: %d", stat)
|
||||
{
|
||||
bool32 data = TRUE;
|
||||
SetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat, &data);
|
||||
CalculateMonStats(&gPlayerParty[partyIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
CalculatePlayerPartyCount();
|
||||
assertf(partyIndex < gPlayerPartyCount, "invalid party index: %d", partyIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool32 data = TRUE;
|
||||
SetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat, &data);
|
||||
CalculateMonStats(&gPlayerParty[partyIndex]);
|
||||
}
|
||||
|
||||
void HasGigantamaxFactor(struct ScriptContext *ctx)
|
||||
|
|
@ -338,7 +357,6 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
|
|||
int sentToPc;
|
||||
struct Pokemon mon;
|
||||
u32 i;
|
||||
u8 genderRatio = gSpeciesInfo[species].genderRatio;
|
||||
u16 targetSpecies;
|
||||
bool32 isShiny;
|
||||
|
||||
|
|
@ -353,9 +371,9 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
|
|||
}
|
||||
|
||||
// create a Pokémon with basic data
|
||||
if ((gender == MON_MALE && genderRatio != MON_FEMALE && genderRatio != MON_GENDERLESS)
|
||||
|| (gender == MON_FEMALE && genderRatio != MON_MALE && genderRatio != MON_GENDERLESS)
|
||||
|| (gender == MON_GENDERLESS && genderRatio == MON_GENDERLESS))
|
||||
// TODO: Use another value for "any gender" so that we can report an
|
||||
// error if genderless.
|
||||
if (gender != MON_GENDERLESS)
|
||||
CreateMonWithGenderNatureLetter(&mon, species, level, 32, gender, nature, 0);
|
||||
else
|
||||
CreateMonWithNature(&mon, species, level, 32, nature);
|
||||
|
|
@ -434,36 +452,33 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
|
|||
SetMonData(&mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
|
||||
SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
|
||||
|
||||
if (slot < PARTY_SIZE)
|
||||
if (side == B_SIDE_PLAYER)
|
||||
{
|
||||
if (side == 0)
|
||||
if (slot < PARTY_SIZE)
|
||||
{
|
||||
CopyMon(&gPlayerParty[slot], &mon, sizeof(struct Pokemon));
|
||||
else
|
||||
CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon));
|
||||
sentToPc = MON_GIVEN_TO_PARTY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system.
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
|
||||
break;
|
||||
}
|
||||
if (i >= PARTY_SIZE)
|
||||
{
|
||||
sentToPc = CopyMonToPC(&mon);
|
||||
}
|
||||
else
|
||||
{
|
||||
sentToPc = MON_GIVEN_TO_PARTY;
|
||||
CopyMon(&gPlayerParty[i], &mon, sizeof(mon));
|
||||
gPlayerPartyCount = i + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system.
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
|
||||
break;
|
||||
}
|
||||
if (i >= PARTY_SIZE)
|
||||
{
|
||||
sentToPc = CopyMonToPC(&mon);
|
||||
}
|
||||
else
|
||||
{
|
||||
sentToPc = MON_GIVEN_TO_PARTY;
|
||||
CopyMon(&gPlayerParty[i], &mon, sizeof(mon));
|
||||
gPlayerPartyCount = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (side == 0)
|
||||
{
|
||||
// set pokédex flags
|
||||
nationalDexNum = SpeciesToNationalPokedexNum(species);
|
||||
if (sentToPc != MON_CANT_GIVE)
|
||||
|
|
@ -472,6 +487,15 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
|
|||
GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assertf(slot < PARTY_SIZE, "invalid slot: %d", slot)
|
||||
{
|
||||
return MON_CANT_GIVE;
|
||||
}
|
||||
CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon));
|
||||
sentToPc = MON_GIVEN_TO_PARTY;
|
||||
}
|
||||
|
||||
return sentToPc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1113,12 +1113,6 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex)
|
|||
DATA.trial.aiActionsPlayed[battlerId]++;
|
||||
}
|
||||
|
||||
void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex)
|
||||
{
|
||||
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s: INVALID: %s trying to send out a mon(id: %d) with 0 HP.",
|
||||
gTestRunnerState.test->filename, BattlerIdentifier(battlerId), gBattlerPartyIndexes[battlerId]);
|
||||
}
|
||||
|
||||
static bool32 CheckComparision(s32 val1, s32 val2, u32 cmp)
|
||||
{
|
||||
switch (cmp)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user