Skip to content

Commit

Permalink
updated Conversion 2 mechanics to 5+ (rh-hideout#5453)
Browse files Browse the repository at this point in the history
* updated Conversion 2 mechanics and added the toggle B_UPDATED_CONVERSION_2

* fixes and added new test cases

* bugfixing and added EWRAM u16 gLastUsedMoveType

* update after Pawkkie review

---------

Co-authored-by: wiz1989 <wiz1989@LAPTOP-8Q3TPMGC.localdomain>
  • Loading branch information
wiz1989 and wiz1989 authored Oct 4, 2024
1 parent 91325e8 commit 6f59d26
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 51 deletions.
1 change: 1 addition & 0 deletions include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,7 @@ extern u16 gLastPrintedMoves[MAX_BATTLERS_COUNT];
extern u16 gLastMoves[MAX_BATTLERS_COUNT];
extern u16 gLastLandedMoves[MAX_BATTLERS_COUNT];
extern u16 gLastHitByType[MAX_BATTLERS_COUNT];
extern u16 gLastUsedMoveType[MAX_BATTLERS_COUNT];
extern u16 gLastResultingMoves[MAX_BATTLERS_COUNT];
extern u16 gLockedMoves[MAX_BATTLERS_COUNT];
extern u16 gLastUsedMove;
Expand Down
1 change: 1 addition & 0 deletions include/config/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#define B_RECOIL_IF_MISS_DMG GEN_LATEST // In Gen5+, Jump Kick and High Jump Kick will always do half of the user's max HP when missing.
#define B_KLUTZ_FLING_INTERACTION GEN_LATEST // In Gen5+, Pokémon with the Klutz ability can't use Fling.
#define B_UPDATED_CONVERSION GEN_LATEST // In Gen6+, Conversion changes the user's type to match their first move's. Before, it would choose a move at random.
#define B_UPDATED_CONVERSION_2 GEN_LATEST // In Gen5+, Conversion 2 changes the user's type to a type that resists the last move used by the selected target. Before, it would consider the last move being successfully hit by. Additionally, Struggle is considered Normal type before Gen 5.
#define B_PP_REDUCED_BY_SPITE GEN_LATEST // In Gen4+, Spite reduces the foe's last move's PP by 4, instead of 2 to 5.
#define B_EXTRAPOLATED_MOVE_FLAGS TRUE // Adds move flags to moves that they don't officially have but would likely have if they were in the latest core series game.

Expand Down
4 changes: 4 additions & 0 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ EWRAM_DATA u16 gLastPrintedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastLandedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastHitByType[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastUsedMoveType[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastResultingMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLockedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastUsedMove = 0;
Expand Down Expand Up @@ -3029,6 +3030,7 @@ static void BattleStartClearSetData(void)
gLastMoves[i] = MOVE_NONE;
gLastLandedMoves[i] = MOVE_NONE;
gLastHitByType[i] = 0;
gLastUsedMoveType[i] = 0;
gLastResultingMoves[i] = MOVE_NONE;
gLastHitBy[i] = 0xFF;
gLockedMoves[i] = MOVE_NONE;
Expand Down Expand Up @@ -3207,6 +3209,7 @@ void SwitchInClearSetData(u32 battler)
gLastMoves[battler] = MOVE_NONE;
gLastLandedMoves[battler] = MOVE_NONE;
gLastHitByType[battler] = 0;
gLastUsedMoveType[battler] = 0;
gLastResultingMoves[battler] = MOVE_NONE;
gLastPrintedMoves[battler] = MOVE_NONE;
gLastHitBy[battler] = 0xFF;
Expand Down Expand Up @@ -3336,6 +3339,7 @@ const u8* FaintClearSetData(u32 battler)
gLastMoves[battler] = MOVE_NONE;
gLastLandedMoves[battler] = MOVE_NONE;
gLastHitByType[battler] = 0;
gLastUsedMoveType[battler] = 0;
gLastResultingMoves[battler] = MOVE_NONE;
gLastPrintedMoves[battler] = MOVE_NONE;
gLastHitBy[battler] = 0xFF;
Expand Down
144 changes: 104 additions & 40 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -5916,12 +5916,14 @@ static void Cmd_moveend(void)
gLastMoves[gBattlerAttacker] = gChosenMove;
RecordKnownMove(gBattlerAttacker, gChosenMove);
gLastResultingMoves[gBattlerAttacker] = gCurrentMove;
GET_MOVE_TYPE(gCurrentMove, gLastUsedMoveType[gBattlerAttacker]);
}
}
else
{
gLastMoves[gBattlerAttacker] = MOVE_UNAVAILABLE;
gLastResultingMoves[gBattlerAttacker] = MOVE_UNAVAILABLE;
gLastUsedMoveType[gBattlerAttacker] = 0;
}

if (!(gHitMarker & HITMARKER_FAINTED(gBattlerTarget)))
Expand Down Expand Up @@ -12953,60 +12955,122 @@ static void Cmd_settypetorandomresistance(void)
{
CMD_ARGS(const u8 *failInstr);

if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE
|| gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE)
// Before Gen 5 Conversion 2 only worked on a move the attacker was actually hit by.
// This changed later to the last move used by the selected target.
if (B_UPDATED_CONVERSION_2 < GEN_5)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gBattleMoveEffects[gMovesInfo[gLastLandedMoves[gBattlerAttacker]].effect].twoTurnEffect
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR || gLastHitByType[gBattlerAttacker] == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE
|| gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gBattleMoveEffects[gMovesInfo[gLastLandedMoves[gBattlerAttacker]].effect].twoTurnEffect
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR || gLastHitByType[gBattlerAttacker] == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
u32 i, resistTypes = 0;
u32 hitByType = gLastHitByType[gBattlerAttacker];

for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist.
{
switch (GetTypeModifier(hitByType, i))
{
case UQ_4_12(0):
case UQ_4_12(0.5):
resistTypes |= gBitTable[i];
break;
}
}

while (resistTypes != 0)
{
i = Random() % NUMBER_OF_MON_TYPES;
if (resistTypes & gBitTable[i])
{
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i))
{
resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type.
}
else
{
SET_BATTLER_TYPE(gBattlerAttacker, i);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, i);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
}
}

gBattlescriptCurrInstr = cmd->failInstr;
}
}
else
{
u32 i, resistTypes = 0;
u32 hitByType = gLastHitByType[gBattlerAttacker];

for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist.
if (gLastResultingMoves[gBattlerTarget] == MOVE_NONE
|| gLastResultingMoves[gBattlerTarget] == MOVE_UNAVAILABLE
|| gLastResultingMoves[gBattlerTarget] == MOVE_STRUGGLE)
{
switch (GetTypeModifier(hitByType, i))
{
case UQ_4_12(0):
case UQ_4_12(0.5):
resistTypes |= gBitTable[i];
break;
}
gBattlescriptCurrInstr = cmd->failInstr;
}

while (resistTypes != 0)
else if (IsSemiInvulnerable(gBattlerTarget, gCurrentMove))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gLastUsedMoveType[gBattlerTarget] == TYPE_NONE || gLastUsedMoveType[gBattlerTarget] == TYPE_STELLAR || gLastUsedMoveType[gBattlerTarget] == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
i = Random() % NUMBER_OF_MON_TYPES;
if (resistTypes & gBitTable[i])
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
u32 i, resistTypes = 0;

for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist.
{
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i))
switch (GetTypeModifier(gLastUsedMoveType[gBattlerTarget], i))
{
resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type.
case UQ_4_12(0):
case UQ_4_12(0.5):
resistTypes |= gBitTable[i];
break;
}
else
}

while (resistTypes != 0)
{
i = Random() % NUMBER_OF_MON_TYPES;
if (resistTypes & gBitTable[i])
{
SET_BATTLER_TYPE(gBattlerAttacker, i);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, i);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i))
{
resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type.
}
else
{
SET_BATTLER_TYPE(gBattlerAttacker, i);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, i);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
}
}
}

gBattlescriptCurrInstr = cmd->failInstr;
gBattlescriptCurrInstr = cmd->failInstr;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/data/moves_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -4511,7 +4511,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.type = TYPE_NORMAL,
.accuracy = 0,
.pp = 30,
.target = MOVE_TARGET_USER,
.target = B_UPDATED_MOVE_DATA >= GEN_5 ? MOVE_TARGET_SELECTED : MOVE_TARGET_USER,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_RECOVER_HP },
Expand Down
2 changes: 2 additions & 0 deletions test/battle/gimmick/terastal.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base t
}
}

#if B_UPDATED_CONVERSION_2 < GEN_5
SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move")
{
GIVEN {
Expand All @@ -526,6 +527,7 @@ SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move"
MESSAGE("But it failed!");
}
}
#endif

SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity when Terastallized into the Stellar type")
{
Expand Down
Loading

0 comments on commit 6f59d26

Please sign in to comment.