Skip to content

Commit

Permalink
Roost suppresses the user's Flying-type rather than remove and re-add…
Browse files Browse the repository at this point in the history
… it. Added tests for EFFECT_ROOST. (#3258)

* Fixed Roost clearing type3 when used by a pure Flying-type. (Gen 5+)

* Created a test file for Roost.

* Marked tests as TODO for now.

* Added more tests for HP healed and type changing.

* Created a function to handle Roost's Flying suppression when getting a battler's type. Added more tests.

* Added test for not-yet-aquired Flying-type. Fixed/rewrote some other tests.

* Now using GetBattlerType() in most relevant places. Fixed some tests.

* Added tests for interactions between Roost and Delta Stream, type-changing effects, Grassy Terrain healing, Levitate, Air Balloon, Magnet Rise, and Telekinesis.

* Added test for interaction between Roost and Reflect Type.

* Gen 4 tests merged with Gen 5+ tests.

* Removed errant space.

Co-authored-by: LOuroboros <[email protected]>

---------

Co-authored-by: LOuroboros <[email protected]>
  • Loading branch information
BLourenco and LOuroboros authored Oct 5, 2023
1 parent b18d018 commit 89e4f30
Show file tree
Hide file tree
Showing 6 changed files with 498 additions and 67 deletions.
2 changes: 1 addition & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER
#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0))
#define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0))

#define IS_BATTLER_OF_TYPE(battlerId, type)((gBattleMons[battlerId].type1 == type || gBattleMons[battlerId].type2 == type || (gBattleMons[battlerId].type3 != TYPE_MYSTERY && gBattleMons[battlerId].type3 == type)))
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type)))
#define SET_BATTLER_TYPE(battlerId, type) \
{ \
gBattleMons[battlerId].type1 = type; \
Expand Down
1 change: 1 addition & 0 deletions include/battle_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,5 +248,6 @@ void RemoveConfusionStatus(u32 battler);
u8 GetBattlerGender(u32 battler);
bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2);
u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance);
u8 GetBattlerType(u32 battler, u8 typeIndex);

#endif // GUARD_BATTLE_UTIL_H
18 changes: 9 additions & 9 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2430,9 +2430,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SOAK:
if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)
|| (gBattleMons[battlerDef].type1 == TYPE_WATER
&& gBattleMons[battlerDef].type2 == TYPE_WATER
&& gBattleMons[battlerDef].type3 == TYPE_MYSTERY))
|| (GetBattlerType(battlerDef, 0) == TYPE_WATER
&& GetBattlerType(battlerDef, 1) == TYPE_WATER
&& GetBattlerType(battlerDef, 2) == TYPE_MYSTERY))
score -= 10; // target is already water-only
break;
case EFFECT_THIRD_TYPE:
Expand Down Expand Up @@ -2582,9 +2582,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_SYNCHRONOISE:
//Check holding ring target or is of same type
if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET
|| IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type1)
|| IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type2)
|| IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type3))
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 0))
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 1))
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 2)))
break;
else
score -= 10;
Expand Down Expand Up @@ -3025,9 +3025,9 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SOAK:
if (atkPartnerAbility == ABILITY_WONDER_GUARD
&& (gBattleMons[battlerAtkPartner].type1 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type2 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type3 != TYPE_WATER))
&& (GetBattlerType(battlerAtkPartner, 0) != TYPE_WATER
|| GetBattlerType(battlerAtkPartner, 1) != TYPE_WATER
|| GetBattlerType(battlerAtkPartner, 2) != TYPE_WATER))
{
RETURN_SCORE_PLUS(1);
}
Expand Down
64 changes: 19 additions & 45 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -2126,12 +2126,12 @@ static void Cmd_adjustdamage(void)
// of a move that is Super Effective against a Flying-type Pokémon.
if (gBattleWeather & B_WEATHER_STRONG_WINDS)
{
if ((gBattleMons[gBattlerTarget].type1 == TYPE_FLYING
&& GetTypeModifier(moveType, gBattleMons[gBattlerTarget].type1) >= UQ_4_12(2.0))
|| (gBattleMons[gBattlerTarget].type2 == TYPE_FLYING
&& GetTypeModifier(moveType, gBattleMons[gBattlerTarget].type2) >= UQ_4_12(2.0))
|| (gBattleMons[gBattlerTarget].type3 == TYPE_FLYING
&& GetTypeModifier(moveType, gBattleMons[gBattlerTarget].type3) >= UQ_4_12(2.0)))
if ((GetBattlerType(gBattlerTarget, 0) == TYPE_FLYING
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0)) >= UQ_4_12(2.0))
|| (GetBattlerType(gBattlerTarget, 1) == TYPE_FLYING
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1)) >= UQ_4_12(2.0))
|| (GetBattlerType(gBattlerTarget, 2) == TYPE_FLYING
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2)) >= UQ_4_12(2.0)))
{
gBattlerAbility = gBattlerTarget;
BattleScriptPushCursor();
Expand Down Expand Up @@ -4909,34 +4909,8 @@ static void Cmd_setroost(void)
CMD_ARGS();

gBattleResources->flags->flags[gBattlerAttacker] |= RESOURCE_FLAG_ROOST;

// Pure flying type.
if (gBattleMons[gBattlerAttacker].type1 == TYPE_FLYING && gBattleMons[gBattlerAttacker].type2 == TYPE_FLYING)
{
gBattleStruct->roostTypes[gBattlerAttacker][0] = TYPE_FLYING;
gBattleStruct->roostTypes[gBattlerAttacker][1] = TYPE_FLYING;
#if B_ROOST_PURE_FLYING >= GEN_5
SET_BATTLER_TYPE(gBattlerAttacker, TYPE_NORMAL);
#else
SET_BATTLER_TYPE(gBattlerAttacker, TYPE_MYSTERY);
#endif
}
// Dual type with flying type.
else if (gBattleMons[gBattlerAttacker].type1 == TYPE_FLYING || gBattleMons[gBattlerAttacker].type2 == TYPE_FLYING)
{
gBattleStruct->roostTypes[gBattlerAttacker][0] = gBattleMons[gBattlerAttacker].type1;
gBattleStruct->roostTypes[gBattlerAttacker][1] = gBattleMons[gBattlerAttacker].type2;
if (gBattleMons[gBattlerAttacker].type1 == TYPE_FLYING)
gBattleMons[gBattlerAttacker].type1 = TYPE_MYSTERY;
else if (gBattleMons[gBattlerAttacker].type2 == TYPE_FLYING)
gBattleMons[gBattlerAttacker].type2 = TYPE_MYSTERY;
}
// Non-flying type.
else
{
gBattleStruct->roostTypes[gBattlerAttacker][0] = gBattleMons[gBattlerAttacker].type1;
gBattleStruct->roostTypes[gBattlerAttacker][1] = gBattleMons[gBattlerAttacker].type2;
}
gBattleStruct->roostTypes[gBattlerAttacker][0] = gBattleMons[gBattlerAttacker].type1;
gBattleStruct->roostTypes[gBattlerAttacker][1] = gBattleMons[gBattlerAttacker].type2;

gBattlescriptCurrInstr = cmd->nextInstr;
}
Expand Down Expand Up @@ -9442,35 +9416,35 @@ static void Cmd_various(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gBattleMons[gBattlerTarget].type1 == TYPE_MYSTERY && gBattleMons[gBattlerTarget].type2 != TYPE_MYSTERY)
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = gBattleMons[gBattlerTarget].type2;
gBattleMons[gBattlerAttacker].type2 = gBattleMons[gBattlerTarget].type2;
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 1);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (gBattleMons[gBattlerTarget].type1 != TYPE_MYSTERY && gBattleMons[gBattlerTarget].type2 == TYPE_MYSTERY)
else if (GetBattlerType(gBattlerTarget, 0) != TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = gBattleMons[gBattlerTarget].type1;
gBattleMons[gBattlerAttacker].type2 = gBattleMons[gBattlerTarget].type1;
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 0);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (gBattleMons[gBattlerTarget].type1 == TYPE_MYSTERY && gBattleMons[gBattlerTarget].type2 == TYPE_MYSTERY)
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerAttacker].type1 = gBattleMons[gBattlerTarget].type1;
gBattleMons[gBattlerAttacker].type2 = gBattleMons[gBattlerTarget].type2;
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
}
case VARIOUS_TRY_SOAK:
{
VARIOUS_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].type1 == gBattleMoves[gCurrentMove].type
&& gBattleMons[gBattlerTarget].type2 == gBattleMoves[gCurrentMove].type)
if (GetBattlerType(gBattlerTarget, 0) == gBattleMoves[gCurrentMove].type
&& GetBattlerType(gBattlerTarget, 1) == gBattleMoves[gCurrentMove].type)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
Expand Down
48 changes: 36 additions & 12 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -3065,11 +3065,7 @@ u8 DoBattlerEndTurnEffects(void)
break;
case ENDTURN_ROOST: // Return flying type.
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST)
{
gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_ROOST;
gBattleMons[battler].type1 = gBattleStruct->roostTypes[battler][0];
gBattleMons[battler].type2 = gBattleStruct->roostTypes[battler][1];
}
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_ELECTRIFY:
Expand Down Expand Up @@ -9990,12 +9986,12 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
{
u32 illusionSpecies;

MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1, battlerAtk, recordAbilities);
if (gBattleMons[battlerDef].type2 != gBattleMons[battlerDef].type1)
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type2, battlerAtk, recordAbilities);
if (gBattleMons[battlerDef].type3 != TYPE_MYSTERY && gBattleMons[battlerDef].type3 != gBattleMons[battlerDef].type2
&& gBattleMons[battlerDef].type3 != gBattleMons[battlerDef].type1)
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type3, battlerAtk, recordAbilities);
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0), battlerAtk, recordAbilities);
if (GetBattlerType(battlerDef, 1) != GetBattlerType(battlerDef, 0))
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1), battlerAtk, recordAbilities);
if (GetBattlerType(battlerDef, 2) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 1)
&& GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 0))
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2), battlerAtk, recordAbilities);

if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef)))
TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies);
Expand Down Expand Up @@ -10506,8 +10502,8 @@ bool32 TryBattleFormChange(u32 battler, u16 method)
bool32 DoBattlersShareType(u32 battler1, u32 battler2)
{
s32 i;
u8 types1[3] = {gBattleMons[battler1].type1, gBattleMons[battler1].type2, gBattleMons[battler1].type3};
u8 types2[3] = {gBattleMons[battler2].type1, gBattleMons[battler2].type2, gBattleMons[battler2].type3};
u8 types1[3] = {GetBattlerType(battler1, 0), GetBattlerType(battler1, 1), GetBattlerType(battler1, 2)};
u8 types2[3] = {GetBattlerType(battler2, 0), GetBattlerType(battler2, 1), GetBattlerType(battler2, 2)};

if (types1[2] == TYPE_MYSTERY)
types1[2] = types1[0];
Expand Down Expand Up @@ -11199,3 +11195,31 @@ bool32 IsGen6ExpShareEnabled(void)
return FlagGet(I_EXP_SHARE_FLAG);
#endif
}


u8 GetBattlerType(u32 battler, u8 typeIndex)
{
u16 types[3] = {0};
types[0] = gBattleMons[battler].type1;
types[1] = gBattleMons[battler].type2;
types[2] = gBattleMons[battler].type3;

// Handle Roost's Flying-type suppression
if (typeIndex == 0 || typeIndex == 1)
{
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST)
{
if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING)
#if B_ROOST_PURE_FLYING >= GEN_5
return TYPE_NORMAL;
#else
return TYPE_MYSTERY;
#endif
else
return types[typeIndex] == TYPE_FLYING ? TYPE_MYSTERY : types[typeIndex];
}
}

return types[typeIndex];
}

Loading

0 comments on commit 89e4f30

Please sign in to comment.