Merge remote-tracking branch 'rhh/master' into master_merge
This commit is contained in:
commit
0f3f2915fb
9 changed files with 136 additions and 12 deletions
|
@ -43,7 +43,7 @@
|
||||||
* Migration script available in `migration_scripts/egg_move_refactor.py` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/5040
|
* Migration script available in `migration_scripts/egg_move_refactor.py` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/5040
|
||||||
* Added documentation to `STATIC_ASSERTS` used by the `BoxPokemon` after 1.8.0 by @pkmnsnfrn in https://github.com/rh-hideout/pokeemerald-expansion/pull/4294
|
* Added documentation to `STATIC_ASSERTS` used by the `BoxPokemon` after 1.8.0 by @pkmnsnfrn in https://github.com/rh-hideout/pokeemerald-expansion/pull/4294
|
||||||
* #### Competitive-formatted parties by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/3545
|
* #### Competitive-formatted parties by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/3545
|
||||||
* Can be disabled by setting `COMPETITIVE_PARTY_SYNTAX` to `FALSE` in `include/config/general.h`.
|
* Can be disabled by setting `COMPETITIVE_PARTY_SYNTAX` to `FALSE` in `include/config/general.h`. If migrating from 1.8, remove the first and last lines from `src/data/trainers.h` (`const struct Trainer gTrainers[] = {` and `};` respectively).
|
||||||
* Introduces `trainerproc`, a tool which converts Competitive-formatted parties into Trainer Control-formatted parties.
|
* Introduces `trainerproc`, a tool which converts Competitive-formatted parties into Trainer Control-formatted parties.
|
||||||
* If you made custom changes to the following files and want to use this new format, ***Do not accept the incoming changes for them.*** Instead, use the migration script present in `migration_scripts/convert_parties.py`:
|
* If you made custom changes to the following files and want to use this new format, ***Do not accept the incoming changes for them.*** Instead, use the migration script present in `migration_scripts/convert_parties.py`:
|
||||||
- `src/data/trainers.h`
|
- `src/data/trainers.h`
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1,019 B |
Binary file not shown.
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 949 B |
|
@ -795,6 +795,7 @@ struct BattleStruct
|
||||||
u8 quickClawRandom[MAX_BATTLERS_COUNT];
|
u8 quickClawRandom[MAX_BATTLERS_COUNT];
|
||||||
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
|
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
|
||||||
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
|
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
|
||||||
|
u8 speedTieBreaks; // MAX_BATTLERS_COUNT! values.
|
||||||
u8 boosterEnergyActivates;
|
u8 boosterEnergyActivates;
|
||||||
u8 distortedTypeMatchups;
|
u8 distortedTypeMatchups;
|
||||||
u8 categoryOverride; // for Z-Moves and Max Moves
|
u8 categoryOverride; // for Z-Moves and Max Moves
|
||||||
|
|
|
@ -75,6 +75,7 @@ s8 GetChosenMovePriority(u32 battlerId);
|
||||||
s8 GetMovePriority(u32 battlerId, u16 move);
|
s8 GetMovePriority(u32 battlerId, u16 move);
|
||||||
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
|
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
|
||||||
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2);
|
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2);
|
||||||
|
s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenMoves);
|
||||||
s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves);
|
s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves);
|
||||||
void RunBattleScriptCommands_PopCallbacksStack(void);
|
void RunBattleScriptCommands_PopCallbacksStack(void);
|
||||||
void RunBattleScriptCommands(void);
|
void RunBattleScriptCommands(void);
|
||||||
|
|
|
@ -2722,7 +2722,7 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||||
ADJUST_SCORE(SLOW_KILL);
|
ADJUST_SCORE(SLOW_KILL);
|
||||||
}
|
}
|
||||||
else if (CanTargetFaintAi(battlerDef, battlerAtk)
|
else if (CanTargetFaintAi(battlerDef, battlerAtk)
|
||||||
&& GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
|
&& GetWhichBattlerFasterOrTies(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
|
||||||
&& GetMovePriority(battlerAtk, move) > 0)
|
&& GetMovePriority(battlerAtk, move) > 0)
|
||||||
{
|
{
|
||||||
ADJUST_SCORE(LAST_CHANCE);
|
ADJUST_SCORE(LAST_CHANCE);
|
||||||
|
@ -4117,7 +4117,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||||
if (IsStatBoostingBerry(item) && aiData->hpPercents[battlerAtk] > 60)
|
if (IsStatBoostingBerry(item) && aiData->hpPercents[battlerAtk] > 60)
|
||||||
ADJUST_SCORE(WEAK_EFFECT);
|
ADJUST_SCORE(WEAK_EFFECT);
|
||||||
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
||||||
&& ((GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) == 1 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
&& ((GetWhichBattlerFasterOrTies(battlerAtk, battlerDef, TRUE) == 1 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
||||||
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
||||||
ADJUST_SCORE(WEAK_EFFECT); // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
ADJUST_SCORE(WEAK_EFFECT); // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ static void SpriteCB_UnusedBattleInit(struct Sprite *sprite);
|
||||||
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
||||||
static u32 Crc32B (const u8 *data, u32 size);
|
static u32 Crc32B (const u8 *data, u32 size);
|
||||||
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
|
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
|
||||||
|
static s32 Factorial(s32);
|
||||||
|
|
||||||
EWRAM_DATA u16 gBattle_BG0_X = 0;
|
EWRAM_DATA u16 gBattle_BG0_X = 0;
|
||||||
EWRAM_DATA u16 gBattle_BG0_Y = 0;
|
EWRAM_DATA u16 gBattle_BG0_Y = 0;
|
||||||
|
@ -3807,6 +3808,8 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||||
}
|
}
|
||||||
#endif // TESTING
|
#endif // TESTING
|
||||||
|
|
||||||
|
gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);
|
||||||
|
|
||||||
for (i = 0; i < gBattlersCount; i++)
|
for (i = 0; i < gBattlersCount; i++)
|
||||||
gBattlerByTurnOrder[i] = i;
|
gBattlerByTurnOrder[i] = i;
|
||||||
for (i = 0; i < gBattlersCount - 1; i++)
|
for (i = 0; i < gBattlersCount - 1; i++)
|
||||||
|
@ -3977,6 +3980,8 @@ void BattleTurnPassed(void)
|
||||||
{
|
{
|
||||||
s32 i;
|
s32 i;
|
||||||
|
|
||||||
|
gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);
|
||||||
|
|
||||||
TurnValuesCleanUp(TRUE);
|
TurnValuesCleanUp(TRUE);
|
||||||
if (gBattleOutcome == 0)
|
if (gBattleOutcome == 0)
|
||||||
{
|
{
|
||||||
|
@ -4865,9 +4870,10 @@ s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMov
|
||||||
strikesFirst = 1;
|
strikesFirst = 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (speedBattler1 == speedBattler2 && Random() & 1)
|
if (speedBattler1 == speedBattler2)
|
||||||
{
|
{
|
||||||
strikesFirst = 0; // same speeds, same priorities
|
// same speeds, same priorities
|
||||||
|
strikesFirst = 0;
|
||||||
}
|
}
|
||||||
else if (speedBattler1 < speedBattler2)
|
else if (speedBattler1 < speedBattler2)
|
||||||
{
|
{
|
||||||
|
@ -4898,7 +4904,7 @@ s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMov
|
||||||
return strikesFirst;
|
return strikesFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
|
s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
|
||||||
{
|
{
|
||||||
s32 priority1 = 0, priority2 = 0;
|
s32 priority1 = 0, priority2 = 0;
|
||||||
u32 ability1 = GetBattlerAbility(battler1);
|
u32 ability1 = GetBattlerAbility(battler1);
|
||||||
|
@ -4916,8 +4922,60 @@ s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
|
||||||
priority2 = GetChosenMovePriority(battler2);
|
priority2 = GetChosenMovePriority(battler2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetWhichBattlerFasterArgs(battler1, battler2, ignoreChosenMoves, ability1, ability2,
|
return GetWhichBattlerFasterArgs(
|
||||||
holdEffectBattler1, holdEffectBattler2, speedBattler1, speedBattler2, priority1, priority2);
|
battler1, battler2,
|
||||||
|
ignoreChosenMoves,
|
||||||
|
ability1, ability2,
|
||||||
|
holdEffectBattler1, holdEffectBattler2,
|
||||||
|
speedBattler1, speedBattler2,
|
||||||
|
priority1, priority2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24 == MAX_BATTLERS_COUNT!.
|
||||||
|
// These are the possible orders if all the battlers speed tie. An order
|
||||||
|
// is chosen at the start of the turn.
|
||||||
|
static const u8 sBattlerOrders[24][4] =
|
||||||
|
{
|
||||||
|
{ 0, 1, 2, 3 },
|
||||||
|
{ 0, 1, 3, 2 },
|
||||||
|
{ 0, 2, 1, 3 },
|
||||||
|
{ 0, 2, 3, 1 },
|
||||||
|
{ 0, 3, 1, 2 },
|
||||||
|
{ 0, 3, 2, 1 },
|
||||||
|
{ 1, 0, 2, 3 },
|
||||||
|
{ 1, 0, 3, 2 },
|
||||||
|
{ 1, 2, 0, 3 },
|
||||||
|
{ 1, 2, 3, 0 },
|
||||||
|
{ 1, 3, 0, 2 },
|
||||||
|
{ 1, 3, 2, 0 },
|
||||||
|
{ 2, 0, 1, 3 },
|
||||||
|
{ 2, 0, 3, 1 },
|
||||||
|
{ 2, 1, 0, 3 },
|
||||||
|
{ 2, 1, 3, 0 },
|
||||||
|
{ 2, 3, 0, 1 },
|
||||||
|
{ 2, 3, 1, 0 },
|
||||||
|
{ 3, 0, 1, 2 },
|
||||||
|
{ 3, 0, 2, 1 },
|
||||||
|
{ 3, 1, 0, 2 },
|
||||||
|
{ 3, 1, 2, 0 },
|
||||||
|
{ 3, 2, 0, 1 },
|
||||||
|
{ 3, 2, 1, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
|
||||||
|
{
|
||||||
|
s32 strikesFirst = GetWhichBattlerFasterOrTies(battler1, battler2, ignoreChosenMoves);
|
||||||
|
if (strikesFirst == 0)
|
||||||
|
{
|
||||||
|
s32 order1 = sBattlerOrders[gBattleStruct->speedTieBreaks][battler1];
|
||||||
|
s32 order2 = sBattlerOrders[gBattleStruct->speedTieBreaks][battler2];
|
||||||
|
if (order1 < order2)
|
||||||
|
strikesFirst = 1;
|
||||||
|
else
|
||||||
|
strikesFirst = -1;
|
||||||
|
}
|
||||||
|
return strikesFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetActionsAndBattlersTurnOrder(void)
|
static void SetActionsAndBattlersTurnOrder(void)
|
||||||
|
@ -5890,3 +5948,11 @@ bool32 IsWildMonSmart(void)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static s32 Factorial(s32 n)
|
||||||
|
{
|
||||||
|
s32 f = 1, i;
|
||||||
|
for (i = 2; i <= n; i++)
|
||||||
|
f *= i;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pok
|
||||||
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
|
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
|
||||||
|
|
||||||
GIVEN {
|
GIVEN {
|
||||||
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); Speed(30); }
|
// FIXME: Explicit moves currently required here because Ditto
|
||||||
|
// expects to find Celebrate in slot 1 during the second turn
|
||||||
|
// (after transforming).
|
||||||
|
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); Speed(30); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID, move); }
|
||||||
OPPONENT(SPECIES_DITTO) { Speed(20); }
|
OPPONENT(SPECIES_DITTO) { Speed(20); }
|
||||||
} WHEN {
|
} WHEN {
|
||||||
TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); }
|
TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); }
|
||||||
|
|
|
@ -66,10 +66,9 @@ SINGLE_BATTLE_TEST("Turn order is determined by Speed if priority ties")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie")
|
SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [singles]")
|
||||||
{
|
{
|
||||||
KNOWN_FAILING; // The algorithm is significantly biased.
|
PASSES_RANDOMLY(1, 2, RNG_SPEED_TIE);
|
||||||
PASSES_RANDOMLY(1, 2);
|
|
||||||
GIVEN {
|
GIVEN {
|
||||||
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||||
|
@ -81,6 +80,60 @@ SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [doubles]")
|
||||||
|
{
|
||||||
|
struct BattlePokemon *order[4] = { NULL, NULL, NULL, NULL };
|
||||||
|
u32 a, b, c, d;
|
||||||
|
|
||||||
|
// TODO: Test all of these in a single PASSES_RANDOMLY pass rather
|
||||||
|
// than 24 PARAMETRIZEd passes.
|
||||||
|
PARAMETRIZE { a = 0; b = 1; c = 2; d = 3; }
|
||||||
|
PARAMETRIZE { a = 0; b = 1; c = 3; d = 2; }
|
||||||
|
PARAMETRIZE { a = 0; b = 2; c = 1; d = 3; }
|
||||||
|
PARAMETRIZE { a = 0; b = 2; c = 3; d = 1; }
|
||||||
|
PARAMETRIZE { a = 0; b = 3; c = 1; d = 2; }
|
||||||
|
PARAMETRIZE { a = 0; b = 3; c = 2; d = 1; }
|
||||||
|
PARAMETRIZE { a = 1; b = 0; c = 2; d = 3; }
|
||||||
|
PARAMETRIZE { a = 1; b = 0; c = 3; d = 2; }
|
||||||
|
PARAMETRIZE { a = 1; b = 2; c = 0; d = 3; }
|
||||||
|
PARAMETRIZE { a = 1; b = 2; c = 3; d = 0; }
|
||||||
|
PARAMETRIZE { a = 1; b = 3; c = 0; d = 2; }
|
||||||
|
PARAMETRIZE { a = 1; b = 3; c = 2; d = 0; }
|
||||||
|
PARAMETRIZE { a = 2; b = 0; c = 1; d = 3; }
|
||||||
|
PARAMETRIZE { a = 2; b = 0; c = 3; d = 1; }
|
||||||
|
PARAMETRIZE { a = 2; b = 1; c = 0; d = 3; }
|
||||||
|
PARAMETRIZE { a = 2; b = 1; c = 3; d = 0; }
|
||||||
|
PARAMETRIZE { a = 2; b = 3; c = 0; d = 1; }
|
||||||
|
PARAMETRIZE { a = 2; b = 3; c = 1; d = 0; }
|
||||||
|
PARAMETRIZE { a = 3; b = 0; c = 1; d = 2; }
|
||||||
|
PARAMETRIZE { a = 3; b = 0; c = 2; d = 1; }
|
||||||
|
PARAMETRIZE { a = 3; b = 1; c = 0; d = 2; }
|
||||||
|
PARAMETRIZE { a = 3; b = 1; c = 2; d = 0; }
|
||||||
|
PARAMETRIZE { a = 3; b = 2; c = 0; d = 1; }
|
||||||
|
PARAMETRIZE { a = 3; b = 2; c = 1; d = 0; }
|
||||||
|
|
||||||
|
order[a] = playerLeft;
|
||||||
|
order[b] = playerRight;
|
||||||
|
order[c] = opponentLeft;
|
||||||
|
order[d] = opponentRight;
|
||||||
|
|
||||||
|
PASSES_RANDOMLY(1, 24, RNG_SPEED_TIE);
|
||||||
|
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||||
|
PLAYER(SPECIES_WYNAUT) { Speed(1); }
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||||
|
OPPONENT(SPECIES_WYNAUT) { Speed(1); }
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(playerLeft, MOVE_SPLASH); MOVE(playerRight, MOVE_SPLASH); MOVE(opponentLeft, MOVE_SPLASH); MOVE(opponentRight, MOVE_SPLASH); }
|
||||||
|
} SCENE {
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[0]);
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[1]);
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[2]);
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate")
|
SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate")
|
||||||
{
|
{
|
||||||
ASSUME(B_CRIT_CHANCE >= GEN_7);
|
ASSUME(B_CRIT_CHANCE >= GEN_7);
|
||||||
|
|
Loading…
Reference in a new issue