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)
|
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;
|
return TRAINER_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertf(trainerId < TRAINERS_COUNT || IsPartnerTrainerId(trainerId), "invalid trainer: %d", trainerId)
|
||||||
|
{
|
||||||
|
return TRAINER_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
return trainerId;
|
return trainerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,10 @@ struct MoveInfo
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
u16 stringId;
|
u16 stringId;
|
||||||
u16 status;
|
union {
|
||||||
|
u16 status;
|
||||||
|
u16 weather;
|
||||||
|
};
|
||||||
} twoTurnAttack;
|
} twoTurnAttack;
|
||||||
struct {
|
struct {
|
||||||
u16 species;
|
u16 species;
|
||||||
|
|
@ -175,10 +178,12 @@ extern const struct BattleMoveEffect gBattleMoveEffects[];
|
||||||
|
|
||||||
static inline u32 SanitizeMoveId(u32 moveId)
|
static inline u32 SanitizeMoveId(u32 moveId)
|
||||||
{
|
{
|
||||||
if (moveId >= MOVES_COUNT_ALL)
|
assertf(moveId < MOVES_COUNT_ALL, "invalid move: %d", moveId)
|
||||||
|
{
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
else
|
}
|
||||||
return moveId;
|
|
||||||
|
return moveId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const u8 *GetMoveName(u32 moveId)
|
static inline const u8 *GetMoveName(u32 moveId)
|
||||||
|
|
@ -231,12 +236,16 @@ static inline u32 GetMovePP(u32 moveId)
|
||||||
|
|
||||||
static inline u32 GetMoveZEffect(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)
|
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)
|
static inline s32 GetMovePriority(u32 moveId)
|
||||||
|
|
@ -496,17 +505,26 @@ static inline bool32 IsValidApprenticeMove(u32 moveId)
|
||||||
|
|
||||||
static inline u32 GetMoveTwoTurnAttackStringId(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)
|
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)
|
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)
|
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)
|
static inline enum ProtectMethod GetMoveProtectMethod(u32 moveId)
|
||||||
{
|
{
|
||||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.protectMethod;
|
moveId = SanitizeMoveId(moveId);
|
||||||
}
|
enum BattleMoveEffects effect = gMovesInfo[moveId].effect;
|
||||||
|
assertf(effect == EFFECT_PROTECT || effect == EFFECT_ENDURE, "not a protect move: %S", gMovesInfo[moveId].name);
|
||||||
static inline u32 GetMoveTerrainFlag(u32 moveId)
|
return gMovesInfo[moveId].argument.protectMethod;
|
||||||
{
|
|
||||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.moveProperty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 GetMoveEffectArg_Status(u32 moveId)
|
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)
|
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;
|
return gMovesInfo[SanitizeMoveId(moveId)].argument.moveProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 GetMoveEffectArg_HoldEffect(u32 moveId)
|
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)
|
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)
|
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)
|
static inline u32 GetMoveAbsorbPercentage(u32 moveId)
|
||||||
{
|
{
|
||||||
moveId = SanitizeMoveId(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)
|
if (gMovesInfo[moveId].argument.absorbPercentage == 0)
|
||||||
return 50;
|
return 50;
|
||||||
return gMovesInfo[moveId].argument.absorbPercentage;
|
return gMovesInfo[moveId].argument.absorbPercentage;
|
||||||
|
|
@ -569,13 +605,15 @@ static inline u32 GetMoveAbsorbPercentage(u32 moveId)
|
||||||
|
|
||||||
static inline u32 GetMoveRecoil(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)
|
static inline u32 GetMoveNonVolatileStatus(u32 move)
|
||||||
{
|
{
|
||||||
move = SanitizeMoveId(move);
|
move = SanitizeMoveId(move);
|
||||||
switch(GetMoveEffect(move))
|
switch (GetMoveEffect(move))
|
||||||
{
|
{
|
||||||
case EFFECT_NON_VOLATILE_STATUS:
|
case EFFECT_NON_VOLATILE_STATUS:
|
||||||
case EFFECT_YAWN:
|
case EFFECT_YAWN:
|
||||||
|
|
@ -588,12 +626,16 @@ static inline u32 GetMoveNonVolatileStatus(u32 move)
|
||||||
|
|
||||||
static inline u32 GetMoveDamagePercentage(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)
|
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)
|
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)
|
static inline const u8 *GetMoveAnimationScript(u32 moveId)
|
||||||
{
|
{
|
||||||
moveId = SanitizeMoveId(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[MOVE_NONE].battleAnimScript;
|
||||||
}
|
}
|
||||||
return gMovesInfo[moveId].battleAnimScript;
|
return gMovesInfo[moveId].battleAnimScript;
|
||||||
|
|
@ -635,9 +676,8 @@ static inline const u8 *GetMoveAnimationScript(u32 moveId)
|
||||||
static inline const u8 *GetMoveBattleScript(u32 moveId)
|
static inline const u8 *GetMoveBattleScript(u32 moveId)
|
||||||
{
|
{
|
||||||
moveId = SanitizeMoveId(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[EFFECT_PLACEHOLDER].battleScript;
|
||||||
}
|
}
|
||||||
return gBattleMoveEffects[GetMoveEffect(moveId)].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_CheckAiMoveScores(u32 battlerId);
|
||||||
void TestRunner_Battle_AISetScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score);
|
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_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_CheckMemory(void);
|
||||||
|
|
||||||
void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType);
|
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_CheckAiMoveScores(...) (void)0
|
||||||
#define TestRunner_Battle_AISetScore(...) (void)0
|
#define TestRunner_Battle_AISetScore(...) (void)0
|
||||||
#define TestRunner_Battle_AIAdjustScore(...) (void)0
|
#define TestRunner_Battle_AIAdjustScore(...) (void)0
|
||||||
#define TestRunner_Battle_InvalidNoHPMon(...) (void)0
|
|
||||||
|
|
||||||
#define TestRunner_Battle_CheckBattleRecordActionType(...) (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?
|
if (!continuousAnim) // May have been used for debug?
|
||||||
{
|
{
|
||||||
// Debugging - ensure no hanging mon bg tasks
|
assertf(!FuncIsActiveTask(Task_UpdateMonBg), "move %d still has Task_UpdateMonBg active at the end", gAnimMoveIndex);
|
||||||
if (FuncIsActiveTask(Task_UpdateMonBg))
|
|
||||||
DebugPrintf("Move %d animation still has Task_UpdateMonBg active at the end!", gAnimMoveIndex);
|
|
||||||
|
|
||||||
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256);
|
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256);
|
||||||
if (!IsContest())
|
if (!IsContest())
|
||||||
|
|
|
||||||
|
|
@ -1961,8 +1961,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
|
||||||
if (speciesInfo->abilities[abilityNum] == partyData[monIndex].ability)
|
if (speciesInfo->abilities[abilityNum] == partyData[monIndex].ability)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (abilityNum >= maxAbilityNum)
|
assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[partyData[monIndex].ability], speciesInfo->speciesName);
|
||||||
abilityNum = 0;
|
|
||||||
}
|
}
|
||||||
else if (B_TRAINER_MON_RANDOM_ABILITY)
|
else if (B_TRAINER_MON_RANDOM_ABILITY)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -956,22 +956,11 @@ static const struct PickupItem sPickupTable[] =
|
||||||
|
|
||||||
static void ValidateSavedBattlerCounts(void)
|
static void ValidateSavedBattlerCounts(void)
|
||||||
{
|
{
|
||||||
if (gBattleStruct->savedAttackerCount > 0)
|
// More calls to SaveBattlerAttacker than RestoreBattlerAttacker.
|
||||||
{
|
assertf(gBattleStruct->savedAttackerCount == 0, "savedAttackerCount is greater than 0");
|
||||||
if (TESTING)
|
|
||||||
{
|
// More calls to SaveBattlerTarget than RestoreBattlerTarget.
|
||||||
Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!", __FILE__, __LINE__);
|
assertf(gBattleStruct->savedTargetCount == 0, "savedTargetCount is greater than 0");
|
||||||
}
|
|
||||||
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!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool32 NoTargetPresent(u8 battler, u32 move)
|
static bool32 NoTargetPresent(u8 battler, u32 move)
|
||||||
|
|
@ -1145,6 +1134,8 @@ static inline bool32 IsBattlerUsingBeakBlast(u32 battler)
|
||||||
static void Cmd_attackcanceler(void)
|
static void Cmd_attackcanceler(void)
|
||||||
{
|
{
|
||||||
CMD_ARGS();
|
CMD_ARGS();
|
||||||
|
assertf(gBattlerAttacker < gBattlersCount, "invalid gBattlerAttacker: %d", gBattlerAttacker);
|
||||||
|
assertf(gBattlerTarget < gBattlersCount, "invalid gBattlerTarget: %d", gBattlerTarget);
|
||||||
|
|
||||||
if (gBattleStruct->battlerState[gBattlerAttacker].usedEjectItem)
|
if (gBattleStruct->battlerState[gBattlerAttacker].usedEjectItem)
|
||||||
{
|
{
|
||||||
|
|
@ -5862,6 +5853,8 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
|
||||||
static void Cmd_moveend(void)
|
static void Cmd_moveend(void)
|
||||||
{
|
{
|
||||||
CMD_ARGS(u8 endMode, u8 endState);
|
CMD_ARGS(u8 endMode, u8 endState);
|
||||||
|
assertf(gBattlerAttacker < gBattlersCount, "invalid gBattlerAttacker: %d", gBattlerAttacker);
|
||||||
|
assertf(gBattlerTarget < gBattlersCount, "invalid gBattlerTarget: %d", gBattlerTarget);
|
||||||
|
|
||||||
s32 i;
|
s32 i;
|
||||||
bool32 effect = FALSE;
|
bool32 effect = FALSE;
|
||||||
|
|
@ -6878,6 +6871,8 @@ static void Cmd_moveend(void)
|
||||||
gBattleScripting.moveendState++;
|
gBattleScripting.moveendState++;
|
||||||
break;
|
break;
|
||||||
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
|
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)
|
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
|
||||||
gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||||||
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
||||||
|
|
@ -7049,6 +7044,36 @@ static void Cmd_returnatktoball(void)
|
||||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
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)
|
static void Cmd_getswitchedmondata(void)
|
||||||
{
|
{
|
||||||
CMD_ARGS(u8 battler);
|
CMD_ARGS(u8 battler);
|
||||||
|
|
@ -7057,10 +7082,11 @@ static void Cmd_getswitchedmondata(void)
|
||||||
if (gBattleControllerExecFlags)
|
if (gBattleControllerExecFlags)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TESTING
|
enum BattleSide side = GetBattlerSide(battler);
|
||||||
&& gBattlerPartyIndexes[battler] == gBattleStruct->monToSwitchIntoId[battler]
|
assertf(IsValidSwitchIn(side, gBattleStruct->monToSwitchIntoId[battler]))
|
||||||
&& IsBattlerAlive(battler))
|
{
|
||||||
Test_ExitWithResult(TEST_RESULT_ERROR, 0, ":L:%s:%d: battler is trying to switch to themself", __FILE__, __LINE__);
|
gBattleStruct->monToSwitchIntoId[battler] = GetArbitraryValidSwitchIn(side);
|
||||||
|
}
|
||||||
|
|
||||||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
|
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
|
||||||
|
|
||||||
|
|
@ -7088,33 +7114,13 @@ static void Cmd_switchindataupdate(void)
|
||||||
for (i = 0; i < sizeof(struct BattlePokemon); i++)
|
for (i = 0; i < sizeof(struct BattlePokemon); i++)
|
||||||
monData[i] = gBattleResources->bufferB[battler][4 + i];
|
monData[i] = gBattleResources->bufferB[battler][4 + i];
|
||||||
|
|
||||||
// Edge case: the sent out pokemon has 0 HP. This should never happen.
|
enum BattleSide side = GetBattlerSide(battler);
|
||||||
if (!IsBattlerAlive(battler))
|
assertf(IsBattlerAlive(battler))
|
||||||
{
|
{
|
||||||
// If it's a test, mark it as invalid.
|
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler] = GetArbitraryValidSwitchIn(side);
|
||||||
if (gTestRunnerEnabled)
|
BtlController_EmitGetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_ALL_BATTLE, 1u << gBattlerPartyIndexes[battler]);
|
||||||
{
|
MarkBattlerForControllerExec(battler);
|
||||||
TestRunner_Battle_InvalidNoHPMon(battler, gBattlerPartyIndexes[battler]);
|
return;
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gBattleMons[battler].types[0] = GetSpeciesType(gBattleMons[battler].species, 0);
|
gBattleMons[battler].types[0] = GetSpeciesType(gBattleMons[battler].species, 0);
|
||||||
|
|
@ -9243,12 +9249,14 @@ static void Cmd_unused_0x78(void)
|
||||||
static void TryResetProtectUseCounter(u32 battler)
|
static void TryResetProtectUseCounter(u32 battler)
|
||||||
{
|
{
|
||||||
u32 lastMove = gLastResultingMoves[battler];
|
u32 lastMove = gLastResultingMoves[battler];
|
||||||
enum BattleMoveEffects lastEffect = GetMoveEffect(lastMove);
|
|
||||||
if (lastMove == MOVE_UNAVAILABLE)
|
if (lastMove == MOVE_UNAVAILABLE)
|
||||||
{
|
{
|
||||||
gBattleMons[battler].volatiles.protectUses = 0;
|
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)
|
if (GetConfig(CONFIG_ALLY_SWITCH_FAIL_CHANCE) < GEN_9 || lastEffect != EFFECT_ALLY_SWITCH)
|
||||||
gBattleMons[battler].volatiles.protectUses = 0;
|
gBattleMons[battler].volatiles.protectUses = 0;
|
||||||
|
|
@ -13962,18 +13970,22 @@ static void Cmd_callnative(void)
|
||||||
|
|
||||||
void SaveBattlerTarget(u32 battler)
|
void SaveBattlerTarget(u32 battler)
|
||||||
{
|
{
|
||||||
if (gBattleStruct->savedTargetCount < NELEMS(gBattleStruct->savedBattlerTarget))
|
assertf(gBattleStruct->savedTargetCount < ARRAY_COUNT(gBattleStruct->savedBattlerTarget), "Too many savedBattlerTargets")
|
||||||
gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler;
|
{
|
||||||
else
|
return;
|
||||||
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerTarget array size!");
|
}
|
||||||
|
|
||||||
|
gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveBattlerAttacker(u32 battler)
|
void SaveBattlerAttacker(u32 battler)
|
||||||
{
|
{
|
||||||
if (gBattleStruct->savedAttackerCount < NELEMS(gBattleStruct->savedBattlerAttacker))
|
assertf(gBattleStruct->savedAttackerCount < ARRAY_COUNT(gBattleStruct->savedBattlerAttacker), "Too many savedBattlerAttackers")
|
||||||
gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler;
|
{
|
||||||
else
|
return;
|
||||||
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerAttacker array size!");
|
}
|
||||||
|
|
||||||
|
gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BS_SaveTarget(void)
|
void BS_SaveTarget(void)
|
||||||
|
|
@ -13986,19 +13998,15 @@ void BS_SaveTarget(void)
|
||||||
void BS_RestoreTarget(void)
|
void BS_RestoreTarget(void)
|
||||||
{
|
{
|
||||||
NATIVE_ARGS();
|
NATIVE_ARGS();
|
||||||
if (gBattleStruct->savedTargetCount > 0)
|
assertf(gBattleStruct->savedTargetCount > 0, "No savedBattlerTargets")
|
||||||
{
|
{
|
||||||
gBattleStruct->savedTargetCount--;
|
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||||
gBattlerTarget = gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount];
|
return;
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gBattleStruct->savedTargetCount--;
|
||||||
|
gBattlerTarget = gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount];
|
||||||
|
|
||||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14012,19 +14020,14 @@ void BS_SaveAttacker(void)
|
||||||
void BS_RestoreAttacker(void)
|
void BS_RestoreAttacker(void)
|
||||||
{
|
{
|
||||||
NATIVE_ARGS();
|
NATIVE_ARGS();
|
||||||
if (gBattleStruct->savedAttackerCount > 0)
|
assertf(gBattleStruct->savedAttackerCount > 0, "No savedBattlerAttackers")
|
||||||
{
|
{
|
||||||
gBattleStruct->savedAttackerCount--;
|
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||||
gBattlerAttacker = gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount];
|
return;
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gBattleStruct->savedAttackerCount--;
|
||||||
|
gBattlerAttacker = gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount];
|
||||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,9 @@ bool32 EndOrContinueWeather(void)
|
||||||
static u32 CalcBeatUpPower(void)
|
static u32 CalcBeatUpPower(void)
|
||||||
{
|
{
|
||||||
u32 species = gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++];
|
u32 species = gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++];
|
||||||
|
// FIXME: Why call CalcBeatUpPower when 'beatUpSlot' is OOB?
|
||||||
|
if (species == 0xFFFF)
|
||||||
|
return 0;
|
||||||
return (GetSpeciesBaseAttack(species) / 10) + 5;
|
return (GetSpeciesBaseAttack(species) / 10) + 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10517,7 +10520,7 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect)
|
||||||
|
|
||||||
bool32 IsMoveEffectRemoveSpeciesType(u32 move, u32 moveEffect, u32 argument)
|
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)
|
bool32 MoveHasChargeTurnAdditionalEffect(u32 move)
|
||||||
|
|
|
||||||
|
|
@ -5327,15 +5327,21 @@ static void Task_WaitForSliderHeartAnim(u8 taskId)
|
||||||
|
|
||||||
static u16 SanitizeMove(u16 move)
|
static u16 SanitizeMove(u16 move)
|
||||||
{
|
{
|
||||||
if (move >= MOVES_COUNT)
|
assertf(move < MOVES_COUNT, "invalid move: %d", move)
|
||||||
move = MOVE_POUND;
|
{
|
||||||
|
return MOVE_POUND;
|
||||||
|
}
|
||||||
|
|
||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 SanitizeSpecies(u16 species)
|
static u16 SanitizeSpecies(u16 species)
|
||||||
{
|
{
|
||||||
if (species >= NUM_SPECIES)
|
assertf(species < NUM_SPECIES, "invalid species: %d", species)
|
||||||
species = SPECIES_NONE;
|
{
|
||||||
|
return SPECIES_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
return species;
|
return species;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2118,7 +2118,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||||
.sleepTalkBanned = TRUE,
|
.sleepTalkBanned = TRUE,
|
||||||
.instructBanned = 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,
|
.contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL : CONTEST_EFFECT_HIGHLY_APPEALING,
|
||||||
.contestCategory = CONTEST_CATEGORY_COOL,
|
.contestCategory = CONTEST_CATEGORY_COOL,
|
||||||
.contestComboStarterId = 0,
|
.contestComboStarterId = 0,
|
||||||
|
|
@ -3757,6 +3757,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||||
.target = MOVE_TARGET_SELECTED,
|
.target = MOVE_TARGET_SELECTED,
|
||||||
.priority = 0,
|
.priority = 0,
|
||||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||||
|
.argument = { .absorbPercentage = 50 },
|
||||||
.healingMove = B_HEAL_BLOCKING >= GEN_6,
|
.healingMove = B_HEAL_BLOCKING >= GEN_6,
|
||||||
.contestEffect = CONTEST_EFFECT_STARTLE_PREV_MONS,
|
.contestEffect = CONTEST_EFFECT_STARTLE_PREV_MONS,
|
||||||
.contestCategory = CONTEST_CATEGORY_SMART,
|
.contestCategory = CONTEST_CATEGORY_SMART,
|
||||||
|
|
@ -5517,6 +5518,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||||
.priority = 2,
|
.priority = 2,
|
||||||
#endif
|
#endif
|
||||||
.category = DAMAGE_CATEGORY_STATUS,
|
.category = DAMAGE_CATEGORY_STATUS,
|
||||||
|
.argument = { .protectMethod = PROTECT_NONE },
|
||||||
.zMove = { .effect = Z_EFFECT_RESET_STATS },
|
.zMove = { .effect = Z_EFFECT_RESET_STATS },
|
||||||
.ignoresProtect = TRUE,
|
.ignoresProtect = TRUE,
|
||||||
.mirrorMoveBanned = TRUE,
|
.mirrorMoveBanned = TRUE,
|
||||||
|
|
@ -16277,7 +16279,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||||
.slicingMove = TRUE,
|
.slicingMove = TRUE,
|
||||||
.sleepTalkBanned = TRUE,
|
.sleepTalkBanned = TRUE,
|
||||||
.instructBanned = 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,
|
.contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
|
||||||
.contestCategory = CONTEST_CATEGORY_TOUGH,
|
.contestCategory = CONTEST_CATEGORY_TOUGH,
|
||||||
.contestComboStarterId = 0,
|
.contestComboStarterId = 0,
|
||||||
|
|
@ -21026,7 +21028,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||||
.target = MOVE_TARGET_SELECTED,
|
.target = MOVE_TARGET_SELECTED,
|
||||||
.priority = 0,
|
.priority = 0,
|
||||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
.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({
|
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||||
.moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1,
|
.moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1,
|
||||||
.self = TRUE,
|
.self = TRUE,
|
||||||
|
|
|
||||||
|
|
@ -1,193 +1,9 @@
|
||||||
#include "decompress_error_handler.h"
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "data.h"
|
#include "decompress_error_handler.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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecompressionError(const u32 *src, enum CompressionError error)
|
void DecompressionError(const u32 *src, enum CompressionError error)
|
||||||
{
|
{
|
||||||
sErrorAddress = (u32)src;
|
assertf(0, "Decompression failed.\nAddress: 0x%p\nError: 0x%x\n", src, error);
|
||||||
sCompressionError = error;
|
|
||||||
SetMainCallback2(DecompressionError_CB2);
|
|
||||||
DecompressionError_CB2();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoDecompressionError(void)
|
void DoDecompressionError(void)
|
||||||
|
|
|
||||||
|
|
@ -2314,12 +2314,13 @@ static void DexNavGuiInit(MainCallback callback)
|
||||||
|
|
||||||
void Task_OpenDexNavFromStartMenu(u8 taskId)
|
void Task_OpenDexNavFromStartMenu(u8 taskId)
|
||||||
{
|
{
|
||||||
if (DEXNAV_ENABLED == FALSE)
|
assertf(DEXNAV_ENABLED, "DexNav was opened when DEXNAV_ENABLED config was disabled! Check include/config/dexnav.h")
|
||||||
{ // must have it enabled to enter
|
{
|
||||||
DebugPrintfLevel(MGBA_LOG_ERROR, "DexNav was opened when DEXNAV_ENABLED config was disabled! Check include/config/dexnav.h");
|
|
||||||
DestroyTask(taskId);
|
DestroyTask(taskId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (!gPaletteFade.active)
|
|
||||||
|
if (!gPaletteFade.active)
|
||||||
{
|
{
|
||||||
CleanupOverworldWindowsAndTilemaps();
|
CleanupOverworldWindowsAndTilemaps();
|
||||||
DexNavGuiInit(CB2_ReturnToFieldWithOpenMenu);
|
DexNavGuiInit(CB2_ReturnToFieldWithOpenMenu);
|
||||||
|
|
|
||||||
|
|
@ -640,7 +640,7 @@ static void SetPositionFromConnection(const struct MapConnection *connection, in
|
||||||
gSaveBlock1Ptr->pos.y = mapHeader->mapLayout->height;
|
gSaveBlock1Ptr->pos.y = mapHeader->mapLayout->height;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DebugPrintfLevel(MGBA_LOG_WARN, "SetPositionFromConnection was passed an invalid direction (%d)!", direction);
|
assertf(0, "invalid direction: %d", direction);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -664,23 +664,21 @@ bool8 CameraMove(int x, int y)
|
||||||
old_x = gSaveBlock1Ptr->pos.x;
|
old_x = gSaveBlock1Ptr->pos.x;
|
||||||
old_y = gSaveBlock1Ptr->pos.y;
|
old_y = gSaveBlock1Ptr->pos.y;
|
||||||
connection = GetIncomingConnection(direction, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
|
connection = GetIncomingConnection(direction, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
|
||||||
if (connection)
|
assertf(connection)
|
||||||
{
|
{
|
||||||
SetPositionFromConnection(connection, direction, x, y);
|
return gCamera.active;
|
||||||
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!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return gCamera.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -792,10 +792,12 @@ bool32 RemovePyramidBagItem(u16 itemId, u16 count)
|
||||||
|
|
||||||
static u16 SanitizeItemId(u16 itemId)
|
static u16 SanitizeItemId(u16 itemId)
|
||||||
{
|
{
|
||||||
if (itemId >= ITEMS_COUNT)
|
assertf(itemId < ITEMS_COUNT, "invalid item: %d", itemId)
|
||||||
|
{
|
||||||
return ITEM_NONE;
|
return ITEM_NONE;
|
||||||
else
|
}
|
||||||
return itemId;
|
|
||||||
|
return itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u8 *GetItemName(u16 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 (pos->next == head)
|
||||||
{
|
{
|
||||||
#if TESTING
|
|
||||||
const struct MemBlock *head = HeapHead();
|
const struct MemBlock *head = HeapHead();
|
||||||
const struct MemBlock *block = head;
|
const struct MemBlock *block = head;
|
||||||
do
|
do
|
||||||
|
|
@ -99,13 +99,10 @@ void *AllocInternal(void *heapStart, u32 size, const char *location)
|
||||||
block = block->next;
|
block = block->next;
|
||||||
}
|
}
|
||||||
while (block != head);
|
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
|
#endif
|
||||||
if (location)
|
assertf(pos->next != head, "%s: out of memory trying to allocate %d bytes", location, size)
|
||||||
{
|
{
|
||||||
DebugPrintfLevel(MGBA_LOG_ERROR, "%s: out of memory trying to allocate %d bytes", location, size);
|
|
||||||
}
|
|
||||||
AGB_ASSERT(FALSE);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -534,37 +534,33 @@ void LoadSaveblockObjEventScripts(void)
|
||||||
savObjTemplates[i].script = mapHeaderObjTemplates[i].script;
|
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)
|
void SetObjEventTemplateCoords(u8 localId, s16 x, s16 y)
|
||||||
{
|
{
|
||||||
s32 i;
|
struct ObjectEventTemplate *objectEventTemplate = GetObjectEventTemplate(localId);
|
||||||
struct ObjectEventTemplate *savObjTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
if (objectEventTemplate)
|
||||||
|
|
||||||
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
|
||||||
{
|
{
|
||||||
struct ObjectEventTemplate *objectEventTemplate = &savObjTemplates[i];
|
objectEventTemplate->x = x;
|
||||||
if (objectEventTemplate->localId == localId)
|
objectEventTemplate->y = y;
|
||||||
{
|
|
||||||
objectEventTemplate->x = x;
|
|
||||||
objectEventTemplate->y = y;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetObjEventTemplateMovementType(u8 localId, u8 movementType)
|
void SetObjEventTemplateMovementType(u8 localId, u8 movementType)
|
||||||
{
|
{
|
||||||
s32 i;
|
struct ObjectEventTemplate *objectEventTemplate = GetObjectEventTemplate(localId);
|
||||||
|
if (objectEventTemplate)
|
||||||
struct ObjectEventTemplate *savObjTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
objectEventTemplate->movementType = movementType;
|
||||||
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
|
||||||
{
|
|
||||||
struct ObjectEventTemplate *objectEventTemplate = &savObjTemplates[i];
|
|
||||||
if (objectEventTemplate->localId == localId)
|
|
||||||
{
|
|
||||||
objectEventTemplate->movementType = movementType;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InitMapView(void)
|
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);
|
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)
|
void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter)
|
||||||
{
|
{
|
||||||
u32 personality;
|
u32 personality;
|
||||||
|
u32 genderRatio = gSpeciesInfo[species].genderRatio;
|
||||||
|
|
||||||
if ((u8)(unownLetter - 1) < NUM_UNOWN_FORMS)
|
if ((u8)(unownLetter - 1) < NUM_UNOWN_FORMS)
|
||||||
{
|
{
|
||||||
u16 actualLetter;
|
u16 actualLetter;
|
||||||
|
|
||||||
do
|
while (TRUE)
|
||||||
{
|
{
|
||||||
personality = Random32();
|
personality = Random32();
|
||||||
actualLetter = GET_UNOWN_LETTER(personality);
|
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
|
else
|
||||||
{
|
{
|
||||||
do
|
while (TRUE)
|
||||||
{
|
{
|
||||||
personality = Random32();
|
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);
|
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 personality;
|
||||||
u32 otId;
|
u32 otId;
|
||||||
|
u32 genderRatio = gSpeciesInfo[species].genderRatio;
|
||||||
|
|
||||||
do
|
while (TRUE)
|
||||||
{
|
{
|
||||||
otId = Random32();
|
otId = Random32();
|
||||||
personality = 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);
|
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)
|
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;
|
return SPECIES_NONE;
|
||||||
else
|
}
|
||||||
return species;
|
|
||||||
|
return species;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool32 IsSpeciesEnabled(u16 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)
|
u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
|
||||||
{
|
{
|
||||||
|
assertf(lo <= hi);
|
||||||
return lo + (((hi - lo + 1) * Random()) >> 16);
|
return lo + (((hi - lo + 1) * Random()) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||||
{
|
{
|
||||||
|
assertf(lo <= hi);
|
||||||
LOOP_RANDOM_START;
|
LOOP_RANDOM_START;
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
|
// TODO: assertf to abort after too many iterations.
|
||||||
u32 n = lo + (((hi - lo + 1) * LOOP_RANDOM) >> 16);
|
u32 n = lo + (((hi - lo + 1) * LOOP_RANDOM) >> 16);
|
||||||
if (!reject(n))
|
if (!reject(n))
|
||||||
return 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)
|
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||||
{
|
{
|
||||||
|
assertf(n > 0);
|
||||||
s32 i, targetSum;
|
s32 i, targetSum;
|
||||||
targetSum = (sum * Random()) >> 16;
|
targetSum = (sum * Random()) >> 16;
|
||||||
for (i = 0; i < n - 1; i++)
|
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)
|
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);
|
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)
|
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;
|
ctx->scriptPtr = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -243,9 +243,20 @@ void CanHyperTrain(struct ScriptContext *ctx)
|
||||||
|
|
||||||
Script_RequestEffects(SCREFF_V1);
|
Script_RequestEffects(SCREFF_V1);
|
||||||
|
|
||||||
if (stat < NUM_STATS
|
assertf(stat < NUM_STATS, "invalid stat: %d", stat)
|
||||||
&& partyIndex < PARTY_SIZE
|
{
|
||||||
&& !GetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + 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)
|
&& GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP_IV + stat) < MAX_PER_STAT_IVS)
|
||||||
{
|
{
|
||||||
gSpecialVar_Result = TRUE;
|
gSpecialVar_Result = TRUE;
|
||||||
|
|
@ -263,12 +274,20 @@ void HyperTrain(struct ScriptContext *ctx)
|
||||||
|
|
||||||
Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE);
|
Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE);
|
||||||
|
|
||||||
if (stat < NUM_STATS && partyIndex < PARTY_SIZE)
|
assertf(stat < NUM_STATS, "invalid stat: %d", stat)
|
||||||
{
|
{
|
||||||
bool32 data = TRUE;
|
return;
|
||||||
SetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat, &data);
|
|
||||||
CalculateMonStats(&gPlayerParty[partyIndex]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void HasGigantamaxFactor(struct ScriptContext *ctx)
|
||||||
|
|
@ -338,7 +357,6 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
|
||||||
int sentToPc;
|
int sentToPc;
|
||||||
struct Pokemon mon;
|
struct Pokemon mon;
|
||||||
u32 i;
|
u32 i;
|
||||||
u8 genderRatio = gSpeciesInfo[species].genderRatio;
|
|
||||||
u16 targetSpecies;
|
u16 targetSpecies;
|
||||||
bool32 isShiny;
|
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
|
// create a Pokémon with basic data
|
||||||
if ((gender == MON_MALE && genderRatio != MON_FEMALE && genderRatio != MON_GENDERLESS)
|
// TODO: Use another value for "any gender" so that we can report an
|
||||||
|| (gender == MON_FEMALE && genderRatio != MON_MALE && genderRatio != MON_GENDERLESS)
|
// error if genderless.
|
||||||
|| (gender == MON_GENDERLESS && genderRatio == MON_GENDERLESS))
|
if (gender != MON_GENDERLESS)
|
||||||
CreateMonWithGenderNatureLetter(&mon, species, level, 32, gender, nature, 0);
|
CreateMonWithGenderNatureLetter(&mon, species, level, 32, gender, nature, 0);
|
||||||
else
|
else
|
||||||
CreateMonWithNature(&mon, species, level, 32, nature);
|
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_NAME, gSaveBlock2Ptr->playerName);
|
||||||
SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
|
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));
|
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;
|
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
|
// set pokédex flags
|
||||||
nationalDexNum = SpeciesToNationalPokedexNum(species);
|
nationalDexNum = SpeciesToNationalPokedexNum(species);
|
||||||
if (sentToPc != MON_CANT_GIVE)
|
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);
|
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;
|
return sentToPc;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1113,12 +1113,6 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex)
|
||||||
DATA.trial.aiActionsPlayed[battlerId]++;
|
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)
|
static bool32 CheckComparision(s32 val1, s32 val2, u32 cmp)
|
||||||
{
|
{
|
||||||
switch (cmp)
|
switch (cmp)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user