Fixes Ruin field statuses negation conditions + upcoming cleanup (#8042)

This commit is contained in:
PhallenTree 2025-10-26 21:41:16 +00:00 committed by GitHub
parent e8ad3c20f7
commit 782c559a20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 265 additions and 27 deletions

View File

@ -7723,7 +7723,7 @@ BattleScript_ItemHealHP_End2::
call BattleScript_ItemHealHP_Ret
end2
BattleScript_AirBaloonMsgIn::
BattleScript_AirBalloonMsgIn::
printstring STRINGID_AIRBALLOONFLOAT
waitmessage B_WAIT_TIME_LONG
end3
@ -7733,7 +7733,7 @@ BattleScript_AirBalloonMsgInRet::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AirBaloonMsgPop::
BattleScript_AirBalloonMsgPop::
printstring STRINGID_AIRBALLOONPOP
waitmessage B_WAIT_TIME_LONG
removeitem BS_TARGET

View File

@ -345,9 +345,9 @@ extern const u8 BattleScript_WeaknessPolicy[];
extern const u8 BattleScript_TargetItemStatRaise[];
extern const u8 BattleScript_RockyHelmetActivates[];
extern const u8 BattleScript_ItemHurtEnd2[];
extern const u8 BattleScript_AirBaloonMsgIn[];
extern const u8 BattleScript_AirBalloonMsgIn[];
extern const u8 BattleScript_AirBalloonMsgInRet[];
extern const u8 BattleScript_AirBaloonMsgPop[];
extern const u8 BattleScript_AirBalloonMsgPop[];
extern const u8 BattleScript_ItemHurtRet[];
extern const u8 BattleScript_ToxicOrb[];
extern const u8 BattleScript_FlameOrb[];

View File

@ -279,7 +279,7 @@ void BattleAI_SetupFlags(void)
}
else // Assign ai flags for player for prediction
{
u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT)
u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT)
| GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_POSITION_OPPONENT_RIGHT);
gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = aiFlags;
gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = aiFlags;
@ -2277,7 +2277,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case PROTECT_WIDE_GUARD:
if(!(GetBattlerMoveTargetType(battlerAtk, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH)))
if (!(GetBattlerMoveTargetType(battlerAtk, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH)))
{
ADJUST_SCORE(-10);
decreased = TRUE;

View File

@ -5308,7 +5308,8 @@ void DecideTerastal(u32 battler)
#define takenWithTera altCalcs->takenWithTera
#define takenWithoutTera gAiLogicData->simulatedDmg[opposingBattler][battler]
enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, struct AltTeraCalcs *altCalcs) {
enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, struct AltTeraCalcs *altCalcs)
{
struct Pokemon* party = GetBattlerParty(battler);
// Check how many pokemon we have that could tera

View File

@ -231,7 +231,7 @@ static enum ItemEffect TryKingsRock(u32 battlerAtk, u32 battlerDef, u32 item)
return effect;
}
static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing)
static enum ItemEffect TryAirBalloon(u32 battler, ActivationTiming timing)
{
enum ItemEffect effect = ITEM_NO_EFFECT;
@ -239,7 +239,7 @@ static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing)
{
if (IsBattlerTurnDamaged(battler))
{
BattleScriptCall(BattleScript_AirBaloonMsgPop);
BattleScriptCall(BattleScript_AirBalloonMsgPop);
effect = ITEM_EFFECT_OTHER;
}
}
@ -247,7 +247,7 @@ static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing)
{
gSpecialStatuses[battler].switchInItemDone = TRUE;
if (timing == IsOnSwitchInFirstTurnActivation)
BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn);
BattleScriptPushCursorAndCallback(BattleScript_AirBalloonMsgIn);
else
BattleScriptCall(BattleScript_AirBalloonMsgInRet);
RecordItemEffectBattle(battler, HOLD_EFFECT_AIR_BALLOON);
@ -497,7 +497,7 @@ static enum ItemEffect TryMentalHerb(u32 battler)
return effect;
}
static enum ItemEffect TryThroatSray(u32 battlerAtk)
static enum ItemEffect TryThroatSpray(u32 battlerAtk)
{
enum ItemEffect effect = ITEM_NO_EFFECT;
@ -1110,7 +1110,7 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect
effect = TryKingsRock(itemBattler, battler, item);
break;
case HOLD_EFFECT_AIR_BALLOON:
effect = TryAirBallon(itemBattler, timing);
effect = TryAirBalloon(itemBattler, timing);
break;
case HOLD_EFFECT_ROCKY_HELMET:
effect = TryRockyHelmet(itemBattler, battler);
@ -1146,7 +1146,7 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect
effect = TryMentalHerb(itemBattler);
break;
case HOLD_EFFECT_THROAT_SPRAY:
effect = TryThroatSray(itemBattler);
effect = TryThroatSpray(itemBattler);
break;
case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move
effect = DamagedStatBoostBerryEffect(itemBattler, battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL);

View File

@ -6124,7 +6124,7 @@ static void Cmd_moveend(void)
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0))
effect = TRUE;
}
if(!effect)
if (!effect)
gBattleScripting.moveendState++;
break;
case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize
@ -9129,16 +9129,18 @@ static void Cmd_useitemonopponent(void)
bool32 CanUseLastResort(u8 battler)
{
u32 knownMovesCount = 0, usedMovesCount = 0;
for (u32 i = 0; i < MAX_MON_MOVES; i++)
u32 moveIndex;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (gBattleMons[battler].moves[i] != MOVE_NONE)
knownMovesCount++;
if (GetMoveEffect(gBattleMons[battler].moves[i]) != EFFECT_LAST_RESORT && gDisableStructs[battler].usedMoves & (1u << i)) // Increment used move count for all moves except current Last Resort.
usedMovesCount++;
u32 move = gBattleMons[battler].moves[moveIndex];
// Assumes that an empty slot cannot have a non-empty slot after it
if (move == MOVE_NONE)
break;
// If not Last Resort and has not been used, can't use Last Resort
if (GetMoveEffect(move) != EFFECT_LAST_RESORT && !(gDisableStructs[battler].usedMoves & (1u << moveIndex)))
return FALSE;
}
return (knownMovesCount >= 2 && usedMovesCount >= knownMovesCount - 1);
return moveIndex >= 2; // At least two usable moves that are either Last Resort or have been used
}
static void RemoveAllWeather(void)
@ -17075,7 +17077,7 @@ void BS_TryActivateReceiver(void)
u32 partnerAbility = GetBattlerAbility(gBattlerAbility);
if (IsBattlerAlive(gBattlerAbility)
&& (partnerAbility == ABILITY_RECEIVER || partnerAbility == ABILITY_POWER_OF_ALCHEMY)
&& GetBattlerHoldEffect(battler) != HOLD_EFFECT_ABILITY_SHIELD
&& GetBattlerHoldEffectIgnoreAbility(battler) != HOLD_EFFECT_ABILITY_SHIELD
&& !gAbilitiesInfo[gBattleMons[battler].ability].cantBeCopied)
{
gBattleStruct->tracedAbility[gBattlerAbility] = gBattleMons[battler].ability; // re-using the variable for trace

View File

@ -7421,11 +7421,17 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
static bool32 IsRuinStatusActive(u32 fieldEffect)
{
if (IsNeutralizingGasOnField()) // Neutralizing Gas still blocks Ruin field statuses
return FALSE;
bool32 isNeutralizingGasOnField = IsNeutralizingGasOnField();
for (u32 battler = 0; battler < gBattlersCount; battler++)
{
// Mold Breaker doesn't ignore Ruin field status but Gastro Acid and Neutralizing Gas do
if (gBattleMons[battler].volatiles.gastroAcid)
continue;
if (GetBattlerHoldEffectIgnoreAbility(battler) != HOLD_EFFECT_ABILITY_SHIELD
&& isNeutralizingGasOnField
&& gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS)
continue;
if (GetBattlerVolatile(battler, fieldEffect))
return TRUE;
}

View File

@ -4,6 +4,7 @@
ASSUMPTIONS
{
ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
ASSUME(GetMoveCategory(MOVE_ROUND) == DAMAGE_CATEGORY_SPECIAL);
ASSUME(GetMoveEffect(MOVE_ROLE_PLAY) == EFFECT_ROLE_PLAY);
}
@ -74,4 +75,117 @@ SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battler
}
}
TO_DO_BATTLE_TEST("Beads of Ruin reduce Defense if Wonder Room is active");
DOUBLE_BATTLE_TEST("Beads of Ruin increases damage taken by physical moves in Wonder Room", s16 damage)
{
bool32 useWonderRoom;
u32 move;
PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_SCRATCH; }
PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_ROUND; }
PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_SCRATCH; }
PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_ROUND; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM);
ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveEffect(MOVE_ROUND) != EFFECT_PSYSHOCK);
PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useWonderRoom)
TURN { MOVE(opponentLeft, MOVE_WONDER_ROOM); MOVE(playerRight, move, target: opponentLeft); }
else
TURN { MOVE(playerRight, move, target: opponentLeft); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, move, playerRight);
HP_BAR(opponentLeft, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_GT(results[2].damage, results[0].damage); // In Wonder Room, physical move deals more damage
EXPECT_LT(results[3].damage, results[1].damage); // In Wonder Room, special move deals less damage
}
}
SINGLE_BATTLE_TEST("Beads of Ruin doesn't activate when dragged out by Mold Breaker attacker")
{
u32 ability;
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
PARAMETRIZE { ability = ABILITY_SAND_RUSH; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); }
OPPONENT(SPECIES_EXCADRILL) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent);
if (ability == ABILITY_MOLD_BREAKER)
{
NONE_OF {
ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
}
}
else
{
ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
}
}
}
DOUBLE_BATTLE_TEST("Beads of Ruin's Sp. Def reduction is not ignored by Mold Breaker", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
PARAMETRIZE { ability = ABILITY_SAND_RUSH; }
GIVEN {
PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_EXCADRILL) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_ROUND, target: playerRight); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
HP_BAR(playerRight, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}
DOUBLE_BATTLE_TEST("Beads of Ruin's Sp. Def reduction is ignored by Gastro Acid", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_GASTRO_ACID; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID);
PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, move, target: playerLeft); MOVE(opponentLeft, MOVE_ROUND, target: playerRight); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, move, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
HP_BAR(playerRight, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_LT(results[0].damage, results[1].damage);
}
}

View File

@ -73,3 +73,118 @@ SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battler
MESSAGE("The opposing Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
}
}
DOUBLE_BATTLE_TEST("Sword of Ruin increases damage taken by special moves in Wonder Room", s16 damage)
{
bool32 useWonderRoom;
u32 move;
PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_SCRATCH; }
PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_ROUND; }
PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_SCRATCH; }
PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_ROUND; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM);
ASSUME(GetMoveCategory(MOVE_ROUND) == DAMAGE_CATEGORY_SPECIAL);
ASSUME(GetMoveEffect(MOVE_ROUND) != EFFECT_PSYSHOCK);
PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useWonderRoom)
TURN { MOVE(opponentLeft, MOVE_WONDER_ROOM); MOVE(playerRight, move, target: opponentLeft); }
else
TURN { MOVE(playerRight, move, target: opponentLeft); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, move, playerRight);
HP_BAR(opponentLeft, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_LT(results[2].damage, results[0].damage); // In Wonder Room, physical move deals less damage
EXPECT_GT(results[3].damage, results[1].damage); // In Wonder Room, special move deals more damage
}
}
SINGLE_BATTLE_TEST("Sword of Ruin doesn't activate when dragged out by Mold Breaker attacker")
{
u32 ability;
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
PARAMETRIZE { ability = ABILITY_SAND_RUSH; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); }
OPPONENT(SPECIES_EXCADRILL) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent);
if (ability == ABILITY_MOLD_BREAKER)
{
NONE_OF {
ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
}
}
else
{
ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
}
}
}
DOUBLE_BATTLE_TEST("Sword of Ruin's Defense reduction is not ignored by Mold Breaker", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
PARAMETRIZE { ability = ABILITY_SAND_RUSH; }
GIVEN {
PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_EXCADRILL) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
HP_BAR(playerRight, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}
DOUBLE_BATTLE_TEST("Sword of Ruin's Defense reduction is ignored by Gastro Acid", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_GASTRO_ACID; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID);
PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, move, target: playerLeft); MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
ANIMATION(ANIM_TYPE_MOVE, move, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
HP_BAR(playerRight, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_LT(results[0].damage, results[1].damage);
}
}