Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability Zero to Hero #3542

Merged
merged 3 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -8678,6 +8678,13 @@ BattleScript_CostarActivates::
waitmessage B_WAIT_TIME_LONG
end3

BattleScript_ZeroToHeroActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_ZEROTOHEROTRANSFORMATION
waitmessage B_WAIT_TIME_LONG
end3

BattleScript_AttackWeakenedByStrongWinds::
pause B_WAIT_TIME_SHORT
printstring STRINGID_ATTACKWEAKENEDBSTRONGWINDS
Expand Down
3 changes: 2 additions & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ struct BattleStruct
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face.
bool8 allowedToChangeFormInWeather[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side, used by Ice Face.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend for things like this just use a bitfield. That way you cut down 12 bytes of memory to 2 (1 for each side).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I let it pass for now since we gotta do a general BattleStruct cleanup anyway, a revamp of #2704.

u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.
Expand All @@ -718,6 +718,7 @@ struct BattleStruct
bool8 trainerSlideBeforeFirstTurnMsgDone;
u32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
u32 aiDelayFrames; // Number of frames it took to choose an action.
bool8 transformZeroToHero[PARTY_SIZE][NUM_BATTLE_SIDES];
};

// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,
Expand Down
1 change: 1 addition & 0 deletions include/battle_scripts.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ extern const u8 BattleScript_RuinAbilityActivates[];
extern const u8 BattleScript_CudChewActivates[];
extern const u8 BattleScript_SupremeOverlordActivates[];
extern const u8 BattleScript_CostarActivates[];
extern const u8 BattleScript_ZeroToHeroActivates[];
extern const u8 BattleScript_ToxicDebrisActivates[];
extern const u8 BattleScript_EarthEaterActivates[];
extern const u8 BattleScript_MimicryActivates_End3[];
Expand Down
3 changes: 2 additions & 1 deletion include/constants/battle_string_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,9 @@
#define STRINGID_TEAMSURROUNDEDBYROCKS 683
#define STRINGID_PKMNHURTBYROCKSTHROWN 684
#define STRINGID_MOVEBLOCKEDBYDYNAMAX 685
#define STRINGID_ZEROTOHEROTRANSFORMATION 686

#define BATTLESTRINGS_COUNT 686
#define BATTLESTRINGS_COUNT 687

// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
Expand Down
2 changes: 2 additions & 0 deletions src/battle_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -822,9 +822,11 @@ static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is
static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!");
static const u8 sText_OpportunistCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} copied its\nopponent's stat changes!");
static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!");
static const u8 sText_ZeroToHeroTransformation[] = _("{B_ATK_NAME_WITH_PREFIX} underwent a heroic\ntransformation!");

const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_ZEROTOHEROTRANSFORMATION - BATTLESTRINGS_TABLE_START] = sText_ZeroToHeroTransformation,
[STRINGID_MOVEBLOCKEDBYDYNAMAX - BATTLESTRINGS_TABLE_START] = sText_MoveBlockedByDynamax,
[STRINGID_OPPORTUNISTCOPIED - BATTLESTRINGS_TABLE_START] = sText_OpportunistCopied,
[STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure,
Expand Down
1 change: 1 addition & 0 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -9151,6 +9151,7 @@ static void Cmd_various(void)
case ABILITY_SCHOOLING: case ABILITY_COMATOSE:
case ABILITY_SHIELDS_DOWN: case ABILITY_DISGUISE:
case ABILITY_RKS_SYSTEM: case ABILITY_TRACE:
case ABILITY_ZERO_TO_HERO:
break;
default:
gBattleStruct->tracedAbility[gBattlerAbility] = gBattleMons[battler].ability; // re-using the variable for trace
Expand Down
20 changes: 20 additions & 0 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ static const u16 sSkillSwapBannedAbilities[] =
ABILITY_ICE_FACE,
ABILITY_HUNGER_SWITCH,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sRolePlayBannedAbilities[] =
Expand All @@ -138,6 +139,7 @@ static const u16 sRolePlayBannedAbilities[] =
ABILITY_ICE_FACE,
ABILITY_HUNGER_SWITCH,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sRolePlayBannedAttackerAbilities[] =
Expand All @@ -154,6 +156,7 @@ static const u16 sRolePlayBannedAttackerAbilities[] =
ABILITY_POWER_CONSTRUCT,
ABILITY_ICE_FACE,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sWorrySeedBannedAbilities[] =
Expand All @@ -170,6 +173,7 @@ static const u16 sWorrySeedBannedAbilities[] =
ABILITY_TRUANT,
ABILITY_ICE_FACE,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sGastroAcidBannedAbilities[] =
Expand All @@ -188,6 +192,7 @@ static const u16 sGastroAcidBannedAbilities[] =
ABILITY_SHIELDS_DOWN,
ABILITY_STANCE_CHANGE,
ABILITY_ZEN_MODE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sEntrainmentBannedAttackerAbilities[] =
Expand All @@ -206,6 +211,7 @@ static const u16 sEntrainmentBannedAttackerAbilities[] =
ABILITY_ICE_FACE,
ABILITY_HUNGER_SWITCH,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static const u16 sEntrainmentTargetSimpleBeamBannedAbilities[] =
Expand All @@ -221,6 +227,7 @@ static const u16 sEntrainmentTargetSimpleBeamBannedAbilities[] =
ABILITY_BATTLE_BOND,
ABILITY_ICE_FACE,
ABILITY_GULP_MISSILE,
ABILITY_ZERO_TO_HERO,
};

static u8 CalcBeatUpPower(void)
Expand Down Expand Up @@ -994,6 +1001,7 @@ static const u8 sAbilitiesNotTraced[ABILITIES_COUNT] =
[ABILITY_STANCE_CHANGE] = 1,
[ABILITY_TRACE] = 1,
[ABILITY_ZEN_MODE] = 1,
[ABILITY_ZERO_TO_HERO] = 1,
};

static const u8 sHoldEffectToType[][2] =
Expand Down Expand Up @@ -4695,6 +4703,17 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
break;
case ABILITY_ZERO_TO_HERO:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& gBattleMons[battler].species == SPECIES_PALAFIN_HERO
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be checking the party mon species. Doing it the current way causes the string to be played when this is acquired via Transform.

Copy link
Collaborator

@AsparagusEduardo AsparagusEduardo Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, didn't consider that case. @AlexOn1ine can you make a test and see if using Transform triggers the Zero to Hero popup/message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be checking the party mon species. Doing it the current way causes the string to be played when this is acquired via Transform.

Transform seemed fine but Imposter did play the message.
Hopefully fixed in #3552. Thank you!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly concerning that the message didn't play for Transform but it did for Imposter. Leads me to wonder if other switch-in Abilities aren't triggering after Transform is used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible, we haven't added Transform tests, so that's something to go back and check.

&& !gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)])
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = TRUE;
BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates);
effect++;
}
break;
}
break;
case ABILITYEFFECT_ENDTURN: // 1
Expand Down Expand Up @@ -6078,6 +6097,7 @@ bool32 IsNeutralizingGasBannedAbility(u32 ability)
case ABILITY_ICE_FACE:
case ABILITY_AS_ONE_ICE_RIDER:
case ABILITY_AS_ONE_SHADOW_RIDER:
case ABILITY_ZERO_TO_HERO:
return TRUE;
default:
return FALSE;
Expand Down
2 changes: 2 additions & 0 deletions src/data/pokemon/form_change_table_pointers.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] =
[SPECIES_ENAMORUS_THERIAN] = sEnamorusFormChangeTable,
#endif
#if P_GEN_9_POKEMON == TRUE
[SPECIES_PALAFIN_ZERO] = sPalafinZeroFormChangeTable,
AsparagusEduardo marked this conversation as resolved.
Show resolved Hide resolved
[SPECIES_PALAFIN_HERO] = sPalafinZeroFormChangeTable,
[SPECIES_OGERPON_TEAL_MASK] = sOgerponFormChangeTable,
[SPECIES_OGERPON_WELLSPRING_MASK] = sOgerponFormChangeTable,
[SPECIES_OGERPON_HEARTHFLAME_MASK] = sOgerponFormChangeTable,
Expand Down
6 changes: 6 additions & 0 deletions src/data/pokemon/form_change_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,12 @@ static const struct FormChange sUrshifuRapidStrikeFormChangeTable[] =
{FORM_CHANGE_TERMINATOR},
};

static const struct FormChange sPalafinZeroFormChangeTable[] =
{
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_PALAFIN_HERO},
AlexOn1ine marked this conversation as resolved.
Show resolved Hide resolved
{FORM_CHANGE_TERMINATOR},
};

#endif

#undef WHEN_LEARNED
Expand Down
140 changes: 140 additions & 0 deletions test/battle/ability/zero_to_hero.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "global.h"
#include "test/battle.h"

ASSUMPTIONS
{
ASSUME(P_GEN_9_POKEMON == TRUE);
}

SINGLE_BATTLE_TEST("Zero to Hero transforms Palafin when it switches out")
{
GIVEN {
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
} SCENE {
MESSAGE("Palafin, that's enough! Come back!");
MESSAGE("Go! Wobbuffet!");
MESSAGE("Wobbuffet, that's enough! Come back!");
MESSAGE("Go! Palafin!");
ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO);
MESSAGE("Palafin underwent a heroic transformation!");
} THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); }
}

SINGLE_BATTLE_TEST("Zero to Hero can't be surpressed by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KOFFING) { Ability(ABILITY_NEUTRALIZING_GAS); }
} WHEN {
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO);
MESSAGE("Palafin underwent a heroic transformation!");
} THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); }
}

SINGLE_BATTLE_TEST("Zero to Hero transforms both player and opponent")
{
GIVEN {
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(player, 1); SWITCH(opponent, 1); }
TURN { SWITCH(player, 0); SWITCH(opponent, 0); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO);
MESSAGE("Palafin underwent a heroic transformation!");
ABILITY_POPUP(opponent, ABILITY_ZERO_TO_HERO);
MESSAGE("Foe Palafin underwent a heroic transformation!");
} THEN {
EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO);
EXPECT_EQ(opponent->species, SPECIES_PALAFIN_HERO);
}
}

SINGLE_BATTLE_TEST("Zero to Hero will activate if a switch move is used")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_FLIP_TURN].effect == EFFECT_HIT_ESCAPE);
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FLIP_TURN); SEND_OUT(player, 1); }
TURN { SWITCH(player, 0); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLIP_TURN, player);
ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO);
MESSAGE("Palafin underwent a heroic transformation!");
} THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); }
}

SINGLE_BATTLE_TEST("Gastro Acid, Worry Seed, and Simple Beam fail if the target has the Ability Zero to Hero")
{
u16 move;

PARAMETRIZE { move = MOVE_GASTRO_ACID; }
PARAMETRIZE { move = MOVE_WORRY_SEED; }
PARAMETRIZE { move = MOVE_SIMPLE_BEAM; }

GIVEN {
ASSUME(gBattleMoves[MOVE_GASTRO_ACID].effect == EFFECT_GASTRO_ACID);
ASSUME(gBattleMoves[MOVE_WORRY_SEED].effect == EFFECT_WORRY_SEED);
ASSUME(gBattleMoves[MOVE_SIMPLE_BEAM].effect == EFFECT_SIMPLE_BEAM);
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, move, player);
MESSAGE("But it failed!");
}
}

SINGLE_BATTLE_TEST("Role Play, Skill Swap, and Entrainment fail if either Pokémon has Zero to Hero")
{
u16 move;

PARAMETRIZE { move = MOVE_ROLE_PLAY; }
PARAMETRIZE { move = MOVE_SKILL_SWAP; }
PARAMETRIZE { move = MOVE_ENTRAINMENT; }

GIVEN {
ASSUME(gBattleMoves[MOVE_ROLE_PLAY].effect == EFFECT_ROLE_PLAY);
ASSUME(gBattleMoves[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
ASSUME(gBattleMoves[MOVE_ENTRAINMENT].effect == EFFECT_ENTRAINMENT);
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, move, player);
MESSAGE("But it failed!");
}
}

// Write Trace test and move this one to that file (including every other ability that can't be copied)
SINGLE_BATTLE_TEST("Zero to Hero cannot be copied by Trace")
{
GIVEN {
PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); }
OPPONENT(SPECIES_RALTS) { Ability(ABILITY_TRACE); }
} WHEN {
TURN {}
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_TRACE);
MESSAGE("Foe Ralts Traced Palafin's Zero to Hero!");
}
}
}
Loading