Skip to content

Commit

Permalink
03.08.2024 Master merge (#5083)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bassoonian authored Aug 3, 2024
2 parents 346b29f + 0f3f291 commit 052258a
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs/changelogs/1.9.x/1.9.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
* 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
* 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.
* 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`
Expand Down
Binary file modified graphics/pokemon/urshifu/front.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/pokemon/urshifu/rapid_strike_style/front.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ struct BattleStruct
u8 quickClawRandom[MAX_BATTLERS_COUNT];
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
u8 speedTieBreaks; // MAX_BATTLERS_COUNT! values.
u8 boosterEnergyActivates;
u8 distortedTypeMatchups;
u8 categoryOverride; // for Z-Moves and Max Moves
Expand Down
1 change: 1 addition & 0 deletions include/battle_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ s8 GetChosenMovePriority(u32 battlerId);
s8 GetMovePriority(u32 battlerId, u16 move);
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
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);
void RunBattleScriptCommands_PopCallbacksStack(void);
void RunBattleScriptCommands(void);
Expand Down
4 changes: 2 additions & 2 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2722,7 +2722,7 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(SLOW_KILL);
}
else if (CanTargetFaintAi(battlerDef, battlerAtk)
&& GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
&& GetWhichBattlerFasterOrTies(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
&& GetMovePriority(battlerAtk, move) > 0)
{
ADJUST_SCORE(LAST_CHANCE);
Expand Down Expand Up @@ -4117,7 +4117,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (IsStatBoostingBerry(item) && aiData->hpPercents[battlerAtk] > 60)
ADJUST_SCORE(WEAK_EFFECT);
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)))
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
}
Expand Down
76 changes: 71 additions & 5 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ static void SpriteCB_UnusedBattleInit(struct Sprite *sprite);
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
static u32 Crc32B (const u8 *data, u32 size);
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_Y = 0;
Expand Down Expand Up @@ -3807,6 +3808,8 @@ static void TryDoEventsBeforeFirstTurn(void)
}
#endif // TESTING

gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);

for (i = 0; i < gBattlersCount; i++)
gBattlerByTurnOrder[i] = i;
for (i = 0; i < gBattlersCount - 1; i++)
Expand Down Expand Up @@ -3977,6 +3980,8 @@ void BattleTurnPassed(void)
{
s32 i;

gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);

TurnValuesCleanUp(TRUE);
if (gBattleOutcome == 0)
{
Expand Down Expand Up @@ -4865,9 +4870,10 @@ s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMov
strikesFirst = 1;
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)
{
Expand Down Expand Up @@ -4898,7 +4904,7 @@ s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMov
return strikesFirst;
}

s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
{
s32 priority1 = 0, priority2 = 0;
u32 ability1 = GetBattlerAbility(battler1);
Expand All @@ -4916,8 +4922,60 @@ s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
priority2 = GetChosenMovePriority(battler2);
}

return GetWhichBattlerFasterArgs(battler1, battler2, ignoreChosenMoves, ability1, ability2,
holdEffectBattler1, holdEffectBattler2, speedBattler1, speedBattler2, priority1, priority2);
return GetWhichBattlerFasterArgs(
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)
Expand Down Expand Up @@ -5890,3 +5948,11 @@ bool32 IsWildMonSmart(void)
return FALSE;
#endif
}

static s32 Factorial(s32 n)
{
s32 f = 1, i;
for (i = 2; i <= n; i++)
f *= i;
return f;
}
5 changes: 4 additions & 1 deletion test/battle/ability/comatose.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pok
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }

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); }
} WHEN {
TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); }
Expand Down
59 changes: 56 additions & 3 deletions test/battle/move.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
PASSES_RANDOMLY(1, 2, RNG_SPEED_TIE);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
Expand All @@ -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")
{
ASSUME(B_CRIT_CHANCE >= GEN_7);
Expand Down

0 comments on commit 052258a

Please sign in to comment.