Adds Booster Energy (#4337)

* Adds Booster Energy

* fix string

* fixes strings in tests

* Apply suggestions from code review

---------

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2024-04-05 19:38:50 +02:00 committed by GitHub
parent 4a102dca8e
commit db42d2b5a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 261 additions and 8 deletions

View file

@ -9926,6 +9926,16 @@ BattleScript_BerserkGeneRet_End:
removeitem BS_SCRIPTING
end3
BattleScript_BoosterEnergyEnd2::
playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1
call BattleScript_AbilityPopUp
printstring STRINGID_BOOSTERENERGYACTIVATES
waitmessage B_WAIT_TIME_MED
printstring STRINGID_STATWASHEIGHTENED
waitmessage B_WAIT_TIME_MED
removeitem BS_SCRIPTING
end2
BattleScript_EffectSnow::
attackcanceler
attackstring

View file

@ -778,6 +778,7 @@ struct BattleStruct
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
u8 quickClawRandom[MAX_BATTLERS_COUNT];
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
u8 boosterEnergyActivates;
};
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View file

@ -495,6 +495,7 @@ extern const u8 BattleScript_TheSwampDisappeared[];
extern const u8 BattleScript_ItemRestoreHP_Party[];
extern const u8 BattleScript_EffectPsychicNoise[];
extern const u8 BattleScript_AromaVeilProtectsRet[];
extern const u8 BattleScript_BoosterEnergyEnd2[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View file

@ -707,8 +707,9 @@
#define STRINGID_BIZARREARENACREATED 705
#define STRINGID_BIZARREAREACREATED 706
#define STRINGID_TIDYINGUPCOMPLETE 707
#define STRINGID_BOOSTERENERGYACTIVATES 708
#define BATTLESTRINGS_COUNT 708
#define BATTLESTRINGS_COUNT 709
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View file

@ -3467,6 +3467,7 @@ void SwitchInClearSetData(u32 battler)
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
gBattleStruct->lastMoveFailed &= ~(gBitTable[battler]);
gBattleStruct->palaceFlags &= ~(gBitTable[battler]);
gBattleStruct->boosterEnergyActivates &= ~(gBitTable[battler]);
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
@ -3598,6 +3599,7 @@ const u8* FaintClearSetData(u32 battler)
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
gBattleStruct->palaceFlags &= ~(gBitTable[battler]);
gBattleStruct->boosterEnergyActivates &= ~(gBitTable[battler]);
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{

View file

@ -791,6 +791,7 @@ static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PRE
static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_DEF_NAME_WITH_PREFIX} with power!");
static const u8 sText_SunlightActivatedAbility[] = _("The harsh sunlight activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
static const u8 sText_StatWasHeightened[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1} was heightened!");
static const u8 sText_BoosterEnergyActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s used its Booster Energy\nto activate {B_SCR_ACTIVE_ABILITY}!");
static const u8 sText_ElectricTerrainActivatedAbility[] = _("The Electric Terrain activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
static const u8 sText_AbilityWeakenedSurroundingMonsStat[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nweakened the {B_BUFF1} of\lall surrounding Pokémon!\p");
static const u8 sText_AttackerGainedStrengthFromTheFallen[] = _("{B_ATK_NAME_WITH_PREFIX} gained strength\nfrom the fallen!");
@ -892,6 +893,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_ABILITYWEAKENEDFSURROUNDINGMONSSTAT - BATTLESTRINGS_TABLE_START] = sText_AbilityWeakenedSurroundingMonsStat,
[STRINGID_ELECTRICTERRAINACTIVATEDABILITY - BATTLESTRINGS_TABLE_START] = sText_ElectricTerrainActivatedAbility,
[STRINGID_STATWASHEIGHTENED - BATTLESTRINGS_TABLE_START] = sText_StatWasHeightened,
[STRINGID_BOOSTERENERGYACTIVATES - BATTLESTRINGS_TABLE_START] = sText_BoosterEnergyActivates,
[STRINGID_SUNLIGHTACTIVATEDABILITY - BATTLESTRINGS_TABLE_START] = sText_SunlightActivatedAbility,
[STRINGID_BEINGHITCHARGEDPKMNWITHPOWER - BATTLESTRINGS_TABLE_START] = sText_BeingHitChargedPkmnWithPower,
[STRINGID_ATTACKERSWITCHEDSTATWITHTARGET - BATTLESTRINGS_TABLE_START] = sText_AttackerSwitchedStatWithTarget,
@ -1571,7 +1573,7 @@ const u16 gMentalHerbCureStringIds[] =
[B_MSG_MENTALHERBCURE_DISABLE] = STRINGID_PKMNMOVEDISABLEDNOMORE,
};
const u16 gStartingStatusStringIds[B_MSG_STARTING_STATUS_COUNT] =
const u16 gStartingStatusStringIds[B_MSG_STARTING_STATUS_COUNT] =
{
[B_MSG_TERRAIN_SET_MISTY] = STRINGID_TERRAINBECOMESMISTY,
[B_MSG_TERRAIN_SET_ELECTRIC] = STRINGID_TERRAINBECOMESELECTRIC,

View file

@ -7272,6 +7272,18 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
effect = ITEM_STATS_CHANGE;
break;
case HOLD_EFFECT_BOOSTER_ENERGY:
if (!(gBattleStruct->boosterEnergyActivates & gBitTable[battler])
&& (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !(gBattleWeather & B_WEATHER_SUN))
|| ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))))
{
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
gBattleScripting.battler = battler;
gBattleStruct->boosterEnergyActivates |= gBitTable[battler];
BattleScriptExecute(BattleScript_BoosterEnergyEnd2);
effect = ITEM_EFFECT_OTHER;
}
break;
}
if (effect != 0)
{
@ -7536,6 +7548,18 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, TRUE);
break;
case HOLD_EFFECT_BOOSTER_ENERGY:
if (!(gBattleStruct->boosterEnergyActivates & gBitTable[battler])
&& (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !(gBattleWeather & B_WEATHER_SUN))
|| ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))))
{
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
gBattlerAbility = gBattleScripting.battler = battler;
gBattleStruct->boosterEnergyActivates |= gBitTable[battler];
BattleScriptExecute(BattleScript_BoosterEnergyEnd2);
effect = ITEM_EFFECT_OTHER;
}
break;
}
if (effect != 0)
@ -8940,7 +8964,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
case ABILITY_PROTOSYNTHESIS:
{
u8 atkHighestStat = GetHighestStatId(battlerAtk);
if (weather & B_WEATHER_SUN
if ((weather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerAtk])
&& ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK)))
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3));
}
@ -8948,7 +8972,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
case ABILITY_QUARK_DRIVE:
{
u8 atkHighestStat = GetHighestStatId(battlerAtk);
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN
if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerAtk])
&& ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK)))
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3));
}
@ -9018,7 +9042,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
case ABILITY_PROTOSYNTHESIS:
{
u8 defHighestStat = GetHighestStatId(battlerDef);
if (weather & B_WEATHER_SUN
if ((weather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerDef])
&& ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF)))
modifier = uq4_12_multiply(modifier, UQ_4_12(0.7));
}
@ -9026,7 +9050,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
case ABILITY_QUARK_DRIVE:
{
u8 defHighestStat = GetHighestStatId(battlerDef);
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN
if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerDef])
&& ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF)))
modifier = uq4_12_multiply(modifier, UQ_4_12(0.7));
}
@ -10492,6 +10516,9 @@ bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId)
return FALSE;
else if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
return FALSE;
else if (holdEffect == HOLD_EFFECT_BOOSTER_ENERGY
&& (gSpeciesInfo[gBattleMons[gBattlerAttacker].species].isParadoxForm || gSpeciesInfo[gBattleMons[gBattlerTarget].species].isParadoxForm))
return FALSE;
else
return TRUE;
}

View file

@ -5708,7 +5708,6 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.iconPalIndex = 0,
//FOOTPRINT(Koraidon)
.isLegendary = TRUE,
.isParadoxForm = TRUE,
.levelUpLearnset = sKoraidonLevelUpLearnset,
.teachableLearnset = sKoraidonTeachableLearnset,
},
@ -5764,7 +5763,6 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.iconPalIndex = 2,
//FOOTPRINT(Miraidon)
.isLegendary = TRUE,
.isParadoxForm = TRUE,
.levelUpLearnset = sMiraidonLevelUpLearnset,
.teachableLearnset = sMiraidonTeachableLearnset,
},

View file

@ -0,0 +1,211 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gItemsInfo[ITEM_BOOSTER_ENERGY].holdEffect == HOLD_EFFECT_BOOSTER_ENERGY);
}
SINGLE_BATTLE_TEST("Booster Energy will activate Quark Drive after Electric Terrain ends")
{
GIVEN {
PLAYER(SPECIES_IRON_MOTH) { Attack(100); Defense(100); Speed(100); SpAttack(110); SpDefense(100); Ability(ABILITY_QUARK_DRIVE); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_TAPU_KOKO) { Speed(100); Ability(ABILITY_ELECTRIC_SURGE); };
} WHEN {
TURN {}
TURN {}
TURN {}
TURN {}
TURN {}
} SCENE {
ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Iron Moth's Booster Energy to activate Quark Drive!");
MESSAGE("Iron Moth's Sp. Atk was heightened!");
}
ABILITY_POPUP(player, ABILITY_QUARK_DRIVE);
MESSAGE("The Electric Terrain activated Iron Moth's Quark Drive!");
MESSAGE("Iron Moth's Sp. Atk was heightened!");
MESSAGE("The electricity disappeared from the battlefield.");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ABILITY_POPUP(player, ABILITY_QUARK_DRIVE);
MESSAGE("Iron Moth's used its Booster Energy to activate Quark Drive!");
MESSAGE("Iron Moth's Sp. Atk was heightened!");
}
}
SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunlight ends")
{
GIVEN {
PLAYER(SPECIES_RAGING_BOLT) { Attack(100); Defense(100); Speed(100); SpAttack(110); SpDefense(100); Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_TORKOAL) { Speed(100); Ability(ABILITY_DROUGHT); };
} WHEN {
TURN {}
TURN {}
TURN {}
TURN {}
TURN {}
} SCENE {
ABILITY_POPUP(opponent, ABILITY_DROUGHT);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("RagingBolt's used its Booster Energy to activate Protosynthesis!");
MESSAGE("RagingBolt's Sp. Atk was heightened!");
}
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
MESSAGE("The harsh sunlight activated RagingBolt's Protosynthesis!");
MESSAGE("RagingBolt's Sp. Atk was heightened!");
MESSAGE("The sunlight faded.");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
MESSAGE("RagingBolt's used its Booster Energy to activate Protosynthesis!");
MESSAGE("RagingBolt's Sp. Atk was heightened!");
}
}
SINGLE_BATTLE_TEST("Booster Energy activates Protosynthesis and increases highest stat")
{
u32 attack, defense, speed, spAttack, spDefense;
PARAMETRIZE { attack = 110; defense = 100; speed = 100; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 110; speed = 100; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 110; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 100; spAttack = 110; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 100; spAttack = 100; spDefense = 110; }
GIVEN {
PLAYER(SPECIES_RAGING_BOLT) { Attack(attack); Defense(defense); Speed(speed); SpAttack(spAttack); SpDefense(spDefense); Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); };
} WHEN {
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
MESSAGE("RagingBolt's used its Booster Energy to activate Protosynthesis!");
if (attack == 110)
MESSAGE("RagingBolt's Attack was heightened!");
else if (defense == 110)
MESSAGE("RagingBolt's Defense was heightened!");
else if (speed == 110)
MESSAGE("RagingBolt's Speed was heightened!");
else if (spAttack == 110)
MESSAGE("RagingBolt's Sp. Atk was heightened!");
else if (spDefense == 110)
MESSAGE("RagingBolt's Sp. Def was heightened!");
} THEN {
EXPECT(player->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Booster Energy activates Quark Drive and increases highest stat")
{
u32 attack, defense, speed, spAttack, spDefense;
PARAMETRIZE { attack = 110; defense = 100; speed = 100; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 110; speed = 100; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 110; spAttack = 100; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 100; spAttack = 110; spDefense = 100; }
PARAMETRIZE { attack = 100; defense = 100; speed = 100; spAttack = 100; spDefense = 110; }
GIVEN {
PLAYER(SPECIES_IRON_MOTH) { Attack(attack); Defense(defense); Speed(speed); SpAttack(spAttack); SpDefense(spDefense); Ability(ABILITY_QUARK_DRIVE); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); };
} WHEN {
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
if (attack == 110)
MESSAGE("Iron Moth's Attack was heightened!");
else if (defense == 110)
MESSAGE("Iron Moth's Defense was heightened!");
else if (speed == 110)
MESSAGE("Iron Moth's Speed was heightened!");
else if (spAttack == 110)
MESSAGE("Iron Moth's Sp. Atk was heightened!");
else if (spDefense == 110)
MESSAGE("Iron Moth's Sp. Def was heightened!");
} THEN {
EXPECT(player->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Booster Energy increases special attack by 30% if it is the highest stat", s16 damage)
{
u32 species;
u32 ability;
u32 item;
PARAMETRIZE { species = SPECIES_RAGING_BOLT; ability = ABILITY_PROTOSYNTHESIS; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_RAGING_BOLT; ability = ABILITY_PROTOSYNTHESIS; item = ITEM_BOOSTER_ENERGY; }
PARAMETRIZE { species = SPECIES_IRON_MOTH; ability = ABILITY_QUARK_DRIVE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_IRON_MOTH; ability = ABILITY_QUARK_DRIVE; item = ITEM_BOOSTER_ENERGY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_ROUND].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(species) { Attack(100); Defense(100); Speed(100); SpAttack(110); SpDefense(100); Ability(ability); Item(item); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); };
} WHEN {
TURN { MOVE(player, MOVE_ROUND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Booster Energy increases special defense by 30% if it is the highest stat", s16 damage)
{
u32 species;
u32 ability;
u32 item;
PARAMETRIZE { species = SPECIES_RAGING_BOLT; ability = ABILITY_PROTOSYNTHESIS; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_RAGING_BOLT; ability = ABILITY_PROTOSYNTHESIS; item = ITEM_BOOSTER_ENERGY; }
PARAMETRIZE { species = SPECIES_IRON_MOTH; ability = ABILITY_QUARK_DRIVE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_IRON_MOTH; ability = ABILITY_QUARK_DRIVE; item = ITEM_BOOSTER_ENERGY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_ROUND].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(species) { Attack(100); Defense(100); Speed(100); SpAttack(100); SpDefense(110); Ability(ability); Item(item); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); };
} WHEN {
TURN { MOVE(opponent, MOVE_ROUND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.7), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Booster Energy can't be flung if a Paradox species is involved")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadoxForm == TRUE);
PLAYER(SPECIES_IRON_MOTH);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); }
} WHEN {
TURN { MOVE(opponent, MOVE_FLING); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent);
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Booster Energy can't be tricked if a Paradox species is involved")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadoxForm == TRUE);
PLAYER(SPECIES_IRON_MOTH) { Item(ITEM_BERRY_JUICE); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
MESSAGE("But it failed!");
}
}