Adds RNG tags and tests for Moody and Starf Berry (#6718)

This commit is contained in:
PhallenTree 2025-05-01 09:16:15 +01:00 committed by GitHub
parent d2a4b5ef84
commit fadf472157
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 220 additions and 37 deletions

View File

@ -311,6 +311,8 @@ bool32 IsMoveEffectRemoveSpeciesType(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef);
bool32 MoodyCantRaiseStat(u32 stat);
bool32 MoodyCantLowerStat(u32 stat);
bool32 CanBeSlept(u32 battler, u32 ability, enum SleepClauseBlock isBlockedBySleepClause);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);

View File

@ -12,6 +12,7 @@ enum GenConfigTag
GEN_CONFIG_GALE_WINGS,
GEN_CONFIG_HEAL_BELL_SOUNDPROOF,
GEN_CONFIG_TELEPORT_BEHAVIOR,
GEN_CONFIG_MOODY_STATS,
GEN_CONFIG_COUNT
};

View File

@ -15,6 +15,7 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_GALE_WINGS] = B_GALE_WINGS,
[GEN_CONFIG_HEAL_BELL_SOUNDPROOF] = B_HEAL_BELL_SOUNDPROOF,
[GEN_CONFIG_TELEPORT_BEHAVIOR] = B_TELEPORT_BEHAVIOR,
[GEN_CONFIG_MOODY_STATS] = B_MOODY_ACC_EVASION,
};
#if TESTING

View File

@ -146,11 +146,14 @@ enum RandomTag
RNG_INFATUATION,
RNG_LOADED_DICE,
RNG_METRONOME,
RNG_MOODY_INCREASE,
RNG_MOODY_DECREASE,
RNG_PARALYSIS,
RNG_PICKUP,
RNG_POISON_POINT,
RNG_POISON_TOUCH,
RNG_RAMPAGE_TURNS,
RNG_RANDOM_STAT_UP,
RNG_SECONDARY_EFFECT,
RNG_SECONDARY_EFFECT_2,
RNG_SECONDARY_EFFECT_3,

View File

@ -1604,6 +1604,18 @@ u32 GetBattlerAffectionHearts(u32 battler)
return GetMonAffectionHearts(&party[gBattlerPartyIndexes[battler]]);
}
// gBattlerAttacker is the battler that's trying to raise their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantRaiseStat(u32 stat)
{
return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL);
}
// gBattlerAttacker is the battler that's trying to lower their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantLowerStat(u32 stat)
{
return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL);
}
void TryToRevertMimicryAndFlags(void)
{
u32 i;
@ -5469,7 +5481,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gDisableStructs[battler].isFirstTurn != 2)
{
u32 validToRaise = 0, validToLower = 0;
u32 statsNum = B_MOODY_ACC_EVASION >= GEN_8 ? NUM_STATS : NUM_BATTLE_STATS;
u32 statsNum = GetGenConfig(GEN_CONFIG_MOODY_STATS) >= GEN_8 ? NUM_STATS : NUM_BATTLE_STATS;
for (i = STAT_ATK; i < statsNum; i++)
{
@ -5479,29 +5491,21 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
validToRaise |= 1u << i;
}
if (validToLower != 0 || validToRaise != 0) // Can lower one stat, or can raise one stat
gBattleScripting.statChanger = gBattleScripting.savedStatChanger = 0; // for raising and lowering stat respectively
if (validToRaise) // Find stat to raise
{
gBattleScripting.statChanger = gBattleScripting.savedStatChanger = 0; // for raising and lowering stat respectively
if (validToRaise != 0) // Find stat to raise
{
do
{
i = (Random() % statsNum) + STAT_ATK;
} while (!(validToRaise & (1u << i)));
SET_STATCHANGER(i, 2, FALSE);
validToLower &= ~(1u << i); // Can't lower the same stat as raising.
}
if (validToLower != 0) // Find stat to lower
{
do
{
i = (Random() % statsNum) + STAT_ATK;
} while (!(validToLower & (1u << i)));
SET_STATCHANGER2(gBattleScripting.savedStatChanger, i, 1, TRUE);
}
BattleScriptPushCursorAndCallback(BattleScript_MoodyActivates);
effect++;
i = RandomUniformExcept(RNG_MOODY_INCREASE, STAT_ATK, statsNum - 1, MoodyCantRaiseStat);
SET_STATCHANGER(i, 2, FALSE);
validToLower &= ~(1u << i); // Can't lower the same stat as raising.
}
if (validToLower) // Find stat to lower
{
// MoodyCantLowerStat already checks that both stats are different
i = RandomUniformExcept(RNG_MOODY_DECREASE, STAT_ATK, statsNum - 1, MoodyCantLowerStat);
SET_STATCHANGER2(gBattleScripting.savedStatChanger, i, 1, TRUE);
}
BattleScriptPushCursorAndCallback(BattleScript_MoodyActivates);
effect++;
}
break;
case ABILITY_TRUANT:
@ -6976,23 +6980,24 @@ static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, u32 statId, enum
static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, enum ItemCaseId caseID)
{
s32 i;
s32 stat;
u16 stringId;
for (i = 0; i < NUM_STATS - 1; i++)
for (stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (CompareStat(battler, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
break;
}
if (i != NUM_STATS - 1 && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId))
if (stat != NUM_STATS && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId))
{
u16 battlerAbility = GetBattlerAbility(battler);
do
{
i = Random() % (NUM_STATS - 1);
} while (!CompareStat(battler, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN));
u32 savedAttacker = gBattlerAttacker;
// MoodyCantRaiseStat requires that the battler is set to gBattlerAttacker
gBattlerAttacker = battler;
stat = RandomUniformExcept(RNG_RANDOM_STAT_UP, STAT_ATK, NUM_STATS - 1, MoodyCantRaiseStat);
gBattlerAttacker = savedAttacker;
PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1);
PREPARE_STAT_BUFFER(gBattleTextBuff1, stat);
stringId = (battlerAbility == ABILITY_CONTRARY) ? STRINGID_STATFELL : STRINGID_STATROSE;
gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN;
gBattleTextBuff2[1] = B_BUFF_STRING;
@ -7004,11 +7009,11 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, enum ItemCa
gBattleTextBuff2[7] = EOS;
gEffectBattler = battler;
if (battlerAbility == ABILITY_RIPEN)
SET_STATCHANGER(i + 1, 4, FALSE);
SET_STATCHANGER(stat, 4, FALSE);
else
SET_STATCHANGER(i + 1, 2, FALSE);
SET_STATCHANGER(stat, 2, FALSE);
gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i + 1;
gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + stat;
gBattleScripting.animArg2 = 0;
if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL)
{

View File

@ -0,0 +1,76 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Moody randomly raises the user's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages")
{
u32 config, statsNum;
PARAMETRIZE { config = GEN_8; statsNum = NUM_STATS; }
PARAMETRIZE { config = GEN_7; statsNum = NUM_BATTLE_STATS; }
// HP is not included
PASSES_RANDOMLY(1, statsNum - 1, RNG_MOODY_INCREASE);
GIVEN {
WITH_CONFIG(GEN_CONFIG_MOODY_STATS, config);
PLAYER(SPECIES_OCTILLERY) { Ability(ABILITY_MOODY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_MOODY);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Octillery's Attack sharply rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("Moody randomly lowers the user's Attack, Defense, Sp. Atk, Sp. Def, or Speed by one stage")
{
u32 config, statsNum;
PARAMETRIZE { config = GEN_8; statsNum = NUM_STATS; }
PARAMETRIZE { config = GEN_7; statsNum = NUM_BATTLE_STATS; }
// One stat becomes unavailable due to it already increasing
PASSES_RANDOMLY(1, statsNum - 2, RNG_MOODY_DECREASE);
GIVEN {
WITH_CONFIG(GEN_CONFIG_MOODY_STATS, config);
PLAYER(SPECIES_OCTILLERY) { Ability(ABILITY_MOODY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_MOODY);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Octillery's Attack fell!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
}
}
SINGLE_BATTLE_TEST("Moody randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages and lowers a different stat by one stage")
{
u32 config, statsNum;
PARAMETRIZE { config = GEN_8; statsNum = NUM_STATS; }
PARAMETRIZE { config = GEN_7; statsNum = NUM_BATTLE_STATS; }
PASSES_RANDOMLY(statsNum - 1, statsNum - 1, RNG_MOODY_DECREASE);
GIVEN {
WITH_CONFIG(GEN_CONFIG_MOODY_STATS, config);
PLAYER(SPECIES_OCTILLERY) { Ability(ABILITY_MOODY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_MOODY);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
for (int stat = STAT_ATK; stat < statsNum; stat++)
EXPECT_NE(player->statStages[stat], DEFAULT_STAT_STAGE + 1); // Both raised and lowered
}
}

View File

@ -6,6 +6,101 @@ ASSUMPTIONS
ASSUME(gItemsInfo[ITEM_STARF_BERRY].holdEffect == HOLD_EFFECT_RANDOM_STAT_UP);
}
TO_DO_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages when the holder's HP drop to 1/4 or below")
TO_DO_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages when the holder's HP drop to 1/2 or below if it has Gluttony")
TO_DO_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by four stages when the holder's HP drop to 1/4 or below if it has Riped")
SINGLE_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages")
{
PASSES_RANDOMLY(1, 5, RNG_RANDOM_STAT_UP);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_STARF_BERRY); HP(100); MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Using Starf Berry, the Attack of Wobbuffet sharply rose!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages when the holder's HP drop to 1/4 or below")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_STARF_BERRY); HP(101); MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
int boostedStats = 0;
EXPECT_EQ(player->item, ITEM_NONE);
EXPECT_LE(player->hp * 4, player->maxHP);
for (int stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (player->statStages[stat] == DEFAULT_STAT_STAGE + 2)
boostedStats++;
else
EXPECT_EQ(player->statStages[stat], DEFAULT_STAT_STAGE);
}
EXPECT_EQ(boostedStats, 1);
}
}
SINGLE_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by two stages when the holder's HP drop to 1/2 or below if it has Gluttony")
{
GIVEN {
PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_STARF_BERRY); HP(201); MaxHP(400); Ability(ABILITY_GLUTTONY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
int boostedStats = 0;
EXPECT_EQ(player->item, ITEM_NONE);
EXPECT_LE(player->hp * 2, player->maxHP);
for (int stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (player->statStages[stat] == DEFAULT_STAT_STAGE + 2)
boostedStats++;
else
EXPECT_EQ(player->statStages[stat], DEFAULT_STAT_STAGE);
}
EXPECT_EQ(boostedStats, 1);
}
}
SINGLE_BATTLE_TEST("Starf Berry randomly raises the holder's Attack, Defense, Sp. Atk, Sp. Def, or Speed by four stages when the holder's HP drop to 1/4 or below if it has Ripen")
{
GIVEN {
PLAYER(SPECIES_FLAPPLE) { Item(ITEM_STARF_BERRY); HP(101); MaxHP(400); Ability(ABILITY_RIPEN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player);
ABILITY_POPUP(player, ABILITY_RIPEN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
int boostedStats = 0;
EXPECT_EQ(player->item, ITEM_NONE);
EXPECT_LE(player->hp * 4, player->maxHP);
for (int stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (player->statStages[stat] == DEFAULT_STAT_STAGE + 4)
boostedStats++;
else
EXPECT_EQ(player->statStages[stat], DEFAULT_STAT_STAGE);
}
EXPECT_EQ(boostedStats, 1);
}
}