updated Conversion 2 mechanics to 5+ (#5453)

* 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>
This commit is contained in:
wiz1989 2024-10-04 18:42:15 +02:00 committed by GitHub
parent 91325e83c1
commit 6f59d26753
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 318 additions and 53 deletions

View file

@ -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;

View file

@ -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.

View file

@ -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;
@ -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;
@ -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;
@ -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;

View file

@ -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)))
@ -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))
{
i = Random() % NUMBER_OF_MON_TYPES;
if (resistTypes & gBitTable[i])
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)
{
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.
}
else
{
SET_BATTLER_TYPE(gBattlerAttacker, i);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, i);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
case UQ_4_12(0):
case UQ_4_12(0.5):
resistTypes |= gBitTable[i];
break;
}
}
}
gBattlescriptCurrInstr = cmd->failInstr;
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;
}
}
}

View file

@ -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 },

View file

@ -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 {
@ -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")
{

View file

@ -1,14 +1,207 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move that hit the user (Gen 3-4)");
TO_DO_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move used by the target (Gen 5+)");
TO_DO_BATTLE_TEST("Conversion 2's type change considers the type of moves called by other moves");
TO_DO_BATTLE_TEST("Conversion 2's type change considers dynamic type moves"); // Eg. Weather Ball
TO_DO_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize and Electrify");
TO_DO_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize");
TO_DO_BATTLE_TEST("Conversion 2's type change considers Struggle to be Normal type (Gen 3-4)");
TO_DO_BATTLE_TEST("Conversion 2 fails if the move used is of typeless damage (Gen 5+)");
TO_DO_BATTLE_TEST("Conversion 2's type change considers status moves (Gen 5+)");
TO_DO_BATTLE_TEST("Conversion 2's type change considers Inverse Battles");
TO_DO_BATTLE_TEST("Conversion 2 fails if the move used is Stellar Type");
#if B_UPDATED_CONVERSION_2 < GEN_5
SINGLE_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move that hit the user (Gen 3-4)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Wobbuffet used Ominous Wind!");
// turn 1
ONE_OF {
MESSAGE("Foe Wobbuffet transformed into the Normal type!");
MESSAGE("Foe Wobbuffet transformed into the Dark type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change considers Struggle to be Normal type (Gen 3-4)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_STRUGGLE); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Struggle!");
// turn 2
ONE_OF {
MESSAGE("Wobbuffet transformed into the Steel type!");
MESSAGE("Wobbuffet transformed into the Rock type!");
MESSAGE("Wobbuffet transformed into the Ghost type!");
}
}
}
#endif
#if B_UPDATED_CONVERSION_2 >= GEN_5
SINGLE_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last used target's move (Gen 5+)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Wobbuffet used Ominous Wind!");
// turn 1
ONE_OF {
MESSAGE("Foe Wobbuffet transformed into the Normal type!");
MESSAGE("Foe Wobbuffet transformed into the Dark type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change considers status moves (Gen 5+)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_CURSE); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Curse!");
// turn 2
ONE_OF {
MESSAGE("Wobbuffet transformed into the Normal type!");
MESSAGE("Wobbuffet transformed into the Dark type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change considers the type of moves called by other moves")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_MIRROR_MOVE); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Mirror Move!");
// turn 2
ONE_OF {
MESSAGE("Wobbuffet transformed into the Normal type!");
MESSAGE("Wobbuffet transformed into the Dark type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change considers dynamic type moves")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HAIL); MOVE(opponent, MOVE_WEATHER_BALL); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Weather Ball!");
// turn 2
ONE_OF {
MESSAGE("Wobbuffet transformed into the Steel type!");
MESSAGE("Wobbuffet transformed into the Fire type!");
MESSAGE("Wobbuffet transformed into the Water type!");
MESSAGE("Wobbuffet transformed into the Ice type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize and Electrify")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NORMALIZE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ELECTRIFY); MOVE(opponent, MOVE_POUND); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
TURN { MOVE(player, MOVE_WATER_GUN); MOVE(opponent, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Wobbuffet used Electrify!");
MESSAGE("Foe Wobbuffet used Pound!");
// turn 2
ONE_OF {
MESSAGE("Wobbuffet transformed into the Ground type!");
MESSAGE("Wobbuffet transformed into the Dragon type!");
MESSAGE("Wobbuffet transformed into the Grass type!");
MESSAGE("Wobbuffet transformed into the Electric type!");
}
// turn 3
MESSAGE("Wobbuffet used Water Gun!");
ONE_OF {
MESSAGE("Foe Wobbuffet transformed into the Steel type!");
MESSAGE("Foe Wobbuffet transformed into the Rock type!");
MESSAGE("Foe Wobbuffet transformed into the Ghost type!");
}
}
}
SINGLE_BATTLE_TEST("Conversion 2's type change fails targeting Struggle (Gen 5+)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_STRUGGLE); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Struggle!");
// turn 2
MESSAGE("Wobbuffet used Conversion 2!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Conversion 2 fails if the move used is of typeless damage (Gen 5+)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ENTEI);
} WHEN {
TURN { MOVE(opponent, MOVE_BURN_UP); }
TURN { MOVE(opponent, MOVE_REVELATION_DANCE); }
TURN { MOVE(player, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Foe Entei used Burn Up!");
// turn 2
MESSAGE("Foe Entei used Revelation Dance!");
// turn 3
MESSAGE("Wobbuffet used Conversion 2!");
MESSAGE("But it failed!");
}
}
#endif
SINGLE_BATTLE_TEST("Conversion 2 fails if the targeted move is Stellar Type")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
// turn 1
MESSAGE("Foe Wobbuffet used Conversion 2!");
MESSAGE("But it failed!");
}
}