master merge

This commit is contained in:
AlexOn1ine 2025-07-19 17:01:18 +02:00
commit 90eef35715
36 changed files with 720 additions and 512 deletions

View File

@ -424,8 +424,8 @@ B_TRAINER1_NAME_WITH_CLASS = FD 42
B_TRAINER2_NAME_WITH_CLASS = FD 43
B_PARTNER_NAME_WITH_CLASS = FD 44
B_ATK_TRAINER_NAME_WITH_CLASS = FD 45
B_SCR_TEAM1 = FD 46
B_SCR_TEAM2 = FD 47
B_EFF_TEAM1 = FD 46
B_EFF_TEAM2 = FD 47
@ indicates the end of a town/city name (before " TOWN" or " CITY")
NAME_END = FC 00

View File

@ -2740,12 +2740,6 @@ BattleScript_MoveMissed::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectDarkVoid::
.if B_DARK_VOID_FAIL >= GEN_7
jumpifspecies SPECIES_DARKRAI, BattleScript_EffectNonVolatileStatus
goto BattleScript_PokemonCantUseTheMove
.endif
BattleScript_TerrainPreventsEnd2::
pause B_WAIT_TIME_SHORT
printfromtable gTerrainPreventsStringIds
@ -4543,6 +4537,11 @@ BattleScript_FlatterTryConfuse::
seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_CONFUSION
goto BattleScript_MoveEnd
BattleScript_EffectDarkVoid::
.if B_DARK_VOID_FAIL >= GEN_7
jumpifspecies SPECIES_DARKRAI, BattleScript_EffectNonVolatileStatus
goto BattleScript_PokemonCantUseTheMove
.endif
BattleScript_EffectNonVolatileStatus::
attackcanceler
attackstring
@ -6540,30 +6539,30 @@ BattleScript_UltraBurst::
end3
BattleScript_GulpMissileFormChange::
call BattleScript_AttackerFormChange
call BattleScript_BattlerFormChange
goto BattleScript_FromTwoTurnMovesSecondTurnRet
BattleScript_AttackerFormChange::
BattleScript_BattlerFormChange::
pause 5
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
flushtextbox
BattleScript_AttackerFormChangeNoPopup::
handleformchange BS_ATTACKER, 0
handleformchange BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE
BattleScript_BattlerFormChangeNoPopup:
handleformchange BS_SCRIPTING, 0
handleformchange BS_SCRIPTING, 1
playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE
waitanimation
handleformchange BS_ATTACKER, 2
handleformchange BS_SCRIPTING, 2
return
BattleScript_AttackerFormChangeEnd3::
call BattleScript_AttackerFormChange
BattleScript_BattlerFormChangeEnd3::
call BattleScript_BattlerFormChange
end3
BattleScript_AttackerFormChangeEnd3NoPopup::
call BattleScript_AttackerFormChangeNoPopup
BattleScript_BattlerFormChangeEnd3NoPopup::
call BattleScript_BattlerFormChangeNoPopup
end3
BattleScript_AttackerFormChangeWithStringEnd3::
BattleScript_BattlerFormChangeWithStringEnd3::
pause 5
call BattleScript_AbilityPopUpScripting
flushtextbox
@ -6639,19 +6638,6 @@ BattleScript_TargetFormChangeWithStringNoPopup::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_BattlerFormChangeWithStringEnd3::
pause 5
call BattleScript_AbilityPopUpScripting
flushtextbox
handleformchange BS_SCRIPTING, 0
handleformchange BS_SCRIPTING, 1
playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE, NULL
waitanimation
handleformchange BS_SCRIPTING, 2
printstring STRINGID_PKMNTRANSFORMED
waitmessage B_WAIT_TIME_LONG
end3
BattleScript_IllusionOffAndTerastallization::
call BattleScript_IllusionOff
goto BattleScript_Terastallization
@ -7422,6 +7408,7 @@ BattleScript_IntimidateLoopIncrement:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
restoretarget
restoreattacker
pause B_WAIT_TIME_MED
end3
@ -7450,7 +7437,6 @@ BattleScript_IntimidateInReverse::
BattleScript_SupersweetSyrupActivates::
savetarget
call BattleScript_AbilityPopUp
destroyabilitypopup
printstring STRINGID_SUPERSWEETAROMAWAFTS
waitmessage B_WAIT_TIME_LONG
setbyte gBattlerTarget, 0
@ -7479,6 +7465,7 @@ BattleScript_SupersweetSyrupLoopIncrement:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
restoretarget
restoreattacker
pause B_WAIT_TIME_MED
end3
@ -7623,10 +7610,8 @@ BattleScript_HospitalityActivates::
printstring STRINGID_HOSPITALITYRESTORATION
waitmessage B_WAIT_TIME_LONG
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE
healthbarupdate BS_TARGET
datahpupdate BS_TARGET
restoreattacker
restoretarget
healthbarupdate BS_EFFECT_BATTLER
datahpupdate BS_EFFECT_BATTLER
end3
BattleScript_AttackWeakenedByStrongWinds::
@ -8080,6 +8065,7 @@ BattleScript_FellStingerRaisesAtkEnd:
BattleScript_AttackerAbilityStatRaiseEnd3::
call BattleScript_AttackerAbilityStatRaise
restoreattacker
end3
BattleScript_SwitchInAbilityMsg::

View File

@ -581,7 +581,8 @@ struct BattlerState
u32 pursuitTarget:1;
u32 stompingTantrumTimer:2;
u32 canPickupItem:1;
u32 padding:17;
u32 itemCanBeKnockedOff:1;
u32 padding:16;
// End of Word
};

View File

@ -84,8 +84,8 @@
#define B_TXT_TRAINER2_NAME_WITH_CLASS 0x43
#define B_TXT_PARTNER_NAME_WITH_CLASS 0x44
#define B_TXT_ATK_TRAINER_NAME_WITH_CLASS 0x45
#define B_TXT_SCR_TEAM1 0x46
#define B_TXT_SCR_TEAM2 0x47
#define B_TXT_EFF_TEAM1 0x46
#define B_TXT_EFF_TEAM2 0x47
#define B_BUFF_STRING 0
#define B_BUFF_NUMBER 1

View File

@ -372,10 +372,10 @@ extern const u8 BattleScript_IllusionOffEnd3[];
extern const u8 BattleScript_IllusionOffAndTerastallization[];
extern const u8 BattleScript_DancerActivates[];
extern const u8 BattleScript_AftermathDmg[];
extern const u8 BattleScript_AttackerFormChange[];
extern const u8 BattleScript_AttackerFormChangeEnd3[];
extern const u8 BattleScript_BattlerFormChange[];
extern const u8 BattleScript_BattlerFormChangeEnd3[];
extern const u8 BattleScript_AttackerFormChangeWithString[];
extern const u8 BattleScript_AttackerFormChangeWithStringEnd3[];
extern const u8 BattleScript_BattlerFormChangeWithStringEnd3[];
extern const u8 BattleScript_TargetFormChange[];
extern const u8 BattleScript_AnticipationActivates[];
extern const u8 BattleScript_SlowStartEnds[];
@ -448,7 +448,7 @@ extern const u8 BattleScript_WanderingSpiritActivates[];
extern const u8 BattleScript_MirrorArmorReflect[];
extern const u8 BattleScript_GooeyActivates[];
extern const u8 BattleScript_PastelVeilActivates[];
extern const u8 BattleScript_AttackerFormChangeEnd3NoPopup[];
extern const u8 BattleScript_BattlerFormChangeEnd3NoPopup[];
extern const u8 BattleScript_AttackerFormChangeMoveEffect[];
extern const u8 BattleScript_BothCanNoLongerEscape[];
extern const u8 BattleScript_OctolockEndTurn[];

View File

@ -422,7 +422,6 @@ enum __attribute__((packed)) MoveEffects
MOVE_EFFECT_REMOVE_ARG_TYPE,
MOVE_EFFECT_RECHARGE,
MOVE_EFFECT_RAGE,
MOVE_EFFECT_STEAL_ITEM,
MOVE_EFFECT_PREVENT_ESCAPE,
MOVE_EFFECT_NIGHTMARE,
MOVE_EFFECT_ALL_STATS_UP,
@ -458,8 +457,6 @@ enum __attribute__((packed)) MoveEffects
MOVE_EFFECT_TRAP_BOTH,
MOVE_EFFECT_ROUND,
MOVE_EFFECT_DIRE_CLAW,
MOVE_EFFECT_STEALTH_ROCK,
MOVE_EFFECT_SPIKES,
MOVE_EFFECT_SYRUP_BOMB,
MOVE_EFFECT_FLORAL_HEALING,
MOVE_EFFECT_SECRET_POWER,
@ -473,6 +470,11 @@ enum __attribute__((packed)) MoveEffects
MOVE_EFFECT_LIGHT_SCREEN,
MOVE_EFFECT_SALT_CURE,
MOVE_EFFECT_EERIE_SPELL,
// Max move effects happen earlier in the execution chain.
// For example stealth rock from G-Max Stonesurge is set up before abilities but from Stone Axe after.
// Stone Axe can also fail to set up rocks if user faints where as Stonesurge will always go up.
// This means we need to be careful if we want to re-use those effects for (new) vanilla moves
MOVE_EFFECT_RAISE_TEAM_ATTACK,
MOVE_EFFECT_RAISE_TEAM_DEFENSE,
MOVE_EFFECT_RAISE_TEAM_SPEED,
@ -514,11 +516,14 @@ enum __attribute__((packed)) MoveEffects
MOVE_EFFECT_LOWER_EVASIVENESS_SIDE,
MOVE_EFFECT_AROMATHERAPY,
MOVE_EFFECT_CONFUSE_SIDE,
MOVE_EFFECT_STEELSURGE,
MOVE_EFFECT_STEELSURGE, // Steel type rocks
MOVE_EFFECT_STEALTH_ROCK, // Max Move rocks, not to be confused for rocks set up from Ceasless Edge (same but differ in execution order)
MOVE_EFFECT_TORMENT_SIDE,
MOVE_EFFECT_LOWER_SPEED_2_SIDE,
MOVE_EFFECT_FIRE_SPIN_SIDE,
MOVE_EFFECT_FIXED_POWER,
// Max move effects end. They can be used for (custom) normal moves.
NUM_MOVE_EFFECTS
};

View File

@ -157,6 +157,7 @@ enum __attribute__((packed)) BattleMoveEffects
EFFECT_BRICK_BREAK,
EFFECT_YAWN,
EFFECT_KNOCK_OFF,
EFFECT_STEAL_ITEM,
EFFECT_ENDEAVOR,
EFFECT_POWER_BASED_ON_USER_HP,
EFFECT_SKILL_SWAP,
@ -351,6 +352,8 @@ enum __attribute__((packed)) BattleMoveEffects
EFFECT_LIFE_DEW,
EFFECT_ICE_SPINNER, // Removes terrain unless attacker is removed from field either by fainting or ejected out
EFFECT_STEEL_ROLLER, // Will fail if there is no terrain up but removes it regardless if attacker is removed from field or not
EFFECT_STONE_AXE, // Not to be confused with MOVE_EFFECT_STEALTH_ROCK. They have two different activation timings.
EFFECT_CEASELESS_EDGE, // Same applies to spikes
NUM_BATTLE_MOVE_EFFECTS,
};

View File

@ -142,7 +142,6 @@ enum MoveEndEffects
MOVEEND_ATTACKER_VISIBLE,
MOVEEND_TARGET_VISIBLE,
MOVEEND_ITEM_EFFECTS_TARGET,
MOVEEND_FIRST_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ALL,
MOVEEND_SYMBIOSIS,
MOVEEND_KINGSROCK, // These item effects will occur each strike of a multi-hit move
@ -153,7 +152,7 @@ enum MoveEndEffects
MOVEEND_DEFROST,
MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a spread move
MOVEEND_MULTIHIT_MOVE,
MOVEEND_SECOND_MOVE_BLOCK,
MOVEEND_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ATTACKER,
MOVEEND_ABILITY_BLOCK,
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist

View File

@ -290,7 +290,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
{
if (moveLimitations & (1u << moveIndex))
SET_SCORE(battler, moveIndex, 0);
if (defaultScoreMoves & 1)
else if (defaultScoreMoves & 1)
SET_SCORE(battler, moveIndex, AI_SCORE_DEFAULT);
else
SET_SCORE(battler, moveIndex, 0);
@ -2769,7 +2769,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_MAGNET_RISE:
if (gFieldStatuses & STATUS_FIELD_GRAVITY
|| gDisableStructs[battlerAtk].magnetRiseTimer != 0
|| gDisableStructs[battlerAtk].magnetRiseTimer > gBattleTurnCounter
|| aiData->holdEffects[battlerAtk] == HOLD_EFFECT_IRON_BALL
|| gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_MAGNET_RISE | STATUS3_SMACKED_DOWN)
|| !IsBattlerGrounded(battlerAtk))
@ -5118,6 +5118,69 @@ case EFFECT_GUARD_SPLIT:
}
}
break;
case EFFECT_STEAL_ITEM:
{
bool32 canSteal = FALSE;
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
canSteal = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || IsOnPlayerSide(battlerAtk))
canSteal = TRUE;
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
&& aiData->items[battlerDef] != ITEM_NONE
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
&& !HasMoveWithEffect(battlerAtk, EFFECT_ACROBATICS)
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_NONE:
break;
case HOLD_EFFECT_CHOICE_BAND:
case HOLD_EFFECT_CHOICE_SCARF:
case HOLD_EFFECT_CHOICE_SPECS:
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoison(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurn(battlerAtk, battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_IRON_BALL:
if (HasMoveWithEffect(battlerAtk, EFFECT_FLING))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(WEAK_EFFECT);
break;
}
}
break;
}
break;
case EFFECT_STONE_AXE:
case EFFECT_CEASELESS_EDGE:
if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData));
{
if (gDisableStructs[battlerAtk].isFirstTurn)
ADJUST_SCORE(BEST_EFFECT);
else
ADJUST_SCORE(DECENT_EFFECT);
}
break;
default:
break;
} // move effect checks
@ -5261,60 +5324,7 @@ case EFFECT_GUARD_SPLIT:
else if (GetItemPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
ADJUST_SCORE(DECENT_EFFECT);
break;
case MOVE_EFFECT_STEAL_ITEM:
{
bool32 canSteal = FALSE;
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
canSteal = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || IsOnPlayerSide(battlerAtk))
canSteal = TRUE;
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
&& aiData->items[battlerDef] != ITEM_NONE
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
&& !HasMoveWithEffect(battlerAtk, EFFECT_ACROBATICS)
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_NONE:
break;
case HOLD_EFFECT_CHOICE_BAND:
case HOLD_EFFECT_CHOICE_SCARF:
case HOLD_EFFECT_CHOICE_SPECS:
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoison(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurn(battlerAtk, battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_IRON_BALL:
if (HasMoveWithEffect(battlerAtk, EFFECT_FLING))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(WEAK_EFFECT);
break;
}
}
break;
}
break;
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData));
{
if (gDisableStructs[battlerAtk].isFirstTurn)
@ -5381,7 +5391,6 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
// Effects that are encouraged on the first turn of battle
static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
u8 i;
if (IsTargetingPartner(battlerAtk, battlerDef)
|| gBattleResults.battleTurnCounter != 0)
return score;
@ -5479,27 +5488,10 @@ static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32
case EFFECT_CHILLY_RECEPTION:
case EFFECT_GEOMANCY:
case EFFECT_VICTORY_DANCE:
case EFFECT_CEASELESS_EDGE:
case EFFECT_STONE_AXE:
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_HIT:
{
// TEMPORARY - should applied to all moves regardless of EFFECT
// Consider move effects
u32 additionalEffectCount = GetMoveAdditionalEffectCount(move);
for (i = 0; i < additionalEffectCount; i++)
{
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(move, i);
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
ADJUST_SCORE(DECENT_EFFECT);
break;
default:
break;
}
}
}
default:
break;
}

View File

@ -852,7 +852,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
SetDynamicMoveCategory(battlerAtk, battlerDef, move);
SetTypeBeforeUsingMove(move, battlerAtk);
// We can set those globals because they are going to get rerolled on attack execution
// We can set those globals because they are going to get rerolled on attack execution
gBattleStruct->magnitudeBasePower = 70;
gBattleStruct->presentBasePower = 80;
@ -1654,7 +1654,7 @@ u32 AI_GetSwitchinWeather(struct BattlePokemon battleMon)
return B_WEATHER_NONE;
if (gBattleWeather & B_WEATHER_PRIMAL_ANY)
return gBattleWeather;
// Switchin will introduce new weather
switch(ability)
{
@ -1668,7 +1668,7 @@ u32 AI_GetSwitchinWeather(struct BattlePokemon battleMon)
return B_SNOW_WARNING >= GEN_9 ? B_WEATHER_SNOW : B_WEATHER_HAIL;
default:
return gBattleWeather;
}
}
}
enum WeatherState IsWeatherActive(u32 flags)
@ -4314,7 +4314,7 @@ bool32 IsRecycleEncouragedItem(u32 item)
return FALSE;
}
bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiIsFaster)
static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiIsFaster)
{
s32 i;
u16 *moves = GetMovesArray(battlerId);
@ -4345,13 +4345,41 @@ bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiI
return FALSE;
}
static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, enum StatChange statId, bool32 considerContrary)
static u32 GetStatBeingChanged(enum StatChange statChange)
{
switch(statChange)
{
case STAT_CHANGE_ATK:
case STAT_CHANGE_ATK_2:
return STAT_ATK;
case STAT_CHANGE_DEF:
case STAT_CHANGE_DEF_2:
return STAT_DEF;
case STAT_CHANGE_SPEED:
case STAT_CHANGE_SPEED_2:
return STAT_SPEED;
case STAT_CHANGE_SPATK:
case STAT_CHANGE_SPATK_2:
return STAT_SPATK;
case STAT_CHANGE_SPDEF:
case STAT_CHANGE_SPDEF_2:
return STAT_SPDEF;
case STAT_CHANGE_ACC:
return STAT_ACC;
case STAT_CHANGE_EVASION:
return STAT_EVASION;
}
return 0; // STAT_HP, should never be getting changed
}
static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, enum StatChange statChange, bool32 considerContrary)
{
enum AIScore tempScore = NO_INCREASE;
u32 noOfHitsToFaint = NoOfHitsForTargetToFaintBattler(battlerDef, battlerAtk);
u32 aiIsFaster = AI_IsFaster(battlerAtk, battlerDef, TRUE);
u32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS);
u32 i;
u32 statId = GetStatBeingChanged(statChange);
if (considerContrary && gAiLogicData->abilities[battlerAtk] == ABILITY_CONTRARY)
return NO_INCREASE;
@ -4419,7 +4447,7 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef,
tempScore += WEAK_EFFECT;
}
switch (statId)
switch (statChange)
{
case STAT_CHANGE_ATK:
if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && shouldSetUp)
@ -4482,7 +4510,7 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef,
}
break;
case STAT_CHANGE_ACC:
if (gBattleMons[battlerAtk].statStages[STAT_ACC] <= 3) // Increase only if necessary
if (gBattleMons[battlerAtk].statStages[statId] <= 3) // Increase only if necessary
tempScore += DECENT_EFFECT;
break;
case STAT_CHANGE_EVASION:
@ -4496,14 +4524,14 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef,
return tempScore;
}
u32 IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, enum StatChange statId)
u32 IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, enum StatChange statChange)
{
return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statId, TRUE);
return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statChange, TRUE);
}
u32 IncreaseStatUpScoreContrary(u32 battlerAtk, u32 battlerDef, enum StatChange statId)
u32 IncreaseStatUpScoreContrary(u32 battlerAtk, u32 battlerDef, enum StatChange statChange)
{
return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statId, FALSE);
return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statChange, FALSE);
}
void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)

View File

@ -250,7 +250,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler)
DestroySprite(&gSprites[gBattleControllerData[battler]]);
SetBattlerShadowSpriteCallback(battler, GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES));
SetBattlerShadowSpriteCallback(battler, GetBattlerVisualSpecies(battler));
gBattleSpritesDataPtr->animationData->introAnimActive = FALSE;
gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE;

View File

@ -1901,9 +1901,7 @@ void StartSendOutAnim(u32 battler, bool32 dontClearTransform, bool32 dontClearSu
ClearTemporarySpeciesSpriteData(battler, dontClearTransform, dontClearSubstituteBit);
gBattlerPartyIndexes[battler] = gBattleResources->bufferA[battler][1];
species = GetIllusionMonSpecies(battler);
if (species == SPECIES_NONE)
species = GetMonData(mon, MON_DATA_SPECIES);
species = GetBattlerVisualSpecies(battler);
gBattleControllerData[battler] = CreateInvisibleSpriteWithCallback(SpriteCB_WaitForBattlerBallReleaseAnim);
// Load sprite for opponent only, player sprite is expected to be already loaded.
if (!IsOnPlayerSide(battler))
@ -2219,7 +2217,7 @@ void BtlController_HandleSetRawMonData(u32 battler)
void BtlController_HandleLoadMonSprite(u32 battler)
{
struct Pokemon *mon = GetBattlerMon(battler);
u16 species = GetMonData(mon, MON_DATA_SPECIES);
u16 species = GetBattlerVisualSpecies(battler);
BattleLoadMonSpriteGfx(mon, battler);
SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battler));
@ -2912,7 +2910,7 @@ void TrySetBattlerShadowSpriteCallback(u32 battler)
if (gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary].callback == SpriteCallbackDummy
&& (B_ENEMY_MON_SHADOW_STYLE <= GEN_3 || P_GBA_STYLE_SPECIES_GFX == TRUE
|| gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdSecondary].callback == SpriteCallbackDummy))
SetBattlerShadowSpriteCallback(battler, GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES));
SetBattlerShadowSpriteCallback(battler, GetBattlerVisualSpecies(battler));
}
void TryShinyAnimAfterMonAnim(u32 battler)

View File

@ -481,10 +481,16 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL:
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler) && IsBattlerGrounded(battler))
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN
&& IsBattlerAlive(battler)
&& !IsBattlerAtMaxHp(battler)
&& !(gStatuses3[battler] & (STATUS3_SEMI_INVULNERABLE | STATUS3_HEAL_BLOCK))
&& IsBattlerGrounded(battler))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16);
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = -1;
BattleScriptExecute(BattleScript_GrassyTerrainHeals);
effect = TRUE;
}

View File

@ -1161,7 +1161,7 @@ void CreateEnemyShadowSprite(u32 battler)
{
if (B_ENEMY_MON_SHADOW_STYLE >= GEN_4 && P_GBA_STYLE_SPECIES_GFX == FALSE)
{
u16 species = SanitizeSpeciesId(gBattleMons[battler].species);
u16 species = GetBattlerVisualSpecies(battler);
u8 size = gSpeciesInfo[species].enemyShadowSize;
gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary = CreateSprite(&gSpriteTemplate_EnemyShadow,
@ -1275,7 +1275,7 @@ void SpriteCB_EnemyShadow(struct Sprite *shadowSprite)
}
else if (B_ENEMY_MON_SHADOW_STYLE >= GEN_4 && P_GBA_STYLE_SPECIES_GFX == FALSE)
{
u16 species = SanitizeSpeciesId(gBattleMons[battler].species);
u16 species = GetBattlerVisualSpecies(battler);
xOffset = gSpeciesInfo[species].enemyShadowXOffset + (shadowSprite->tSpriteSide == SPRITE_SIDE_LEFT ? -16 : 16);
yOffset = gSpeciesInfo[species].enemyShadowYOffset + 16;
size = gSpeciesInfo[species].enemyShadowSize;

View File

@ -318,7 +318,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNENDUREDHIT] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} endured the hit!"),
[STRINGID_MAGNITUDESTRENGTH] = COMPOUND_STRING("Magnitude {B_BUFF1}!"),
[STRINGID_PKMNCUTHPMAXEDATTACK] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} cut its own HP and maximized its Attack!"),
[STRINGID_PKMNCOPIEDSTATCHANGES] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} copied {B_SCR_NAME_WITH_PREFIX2}'s stat changes!"),
[STRINGID_PKMNCOPIEDSTATCHANGES] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} copied {B_EFF_NAME_WITH_PREFIX2}'s stat changes!"),
[STRINGID_PKMNGOTFREE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} got free of {B_DEF_NAME_WITH_PREFIX2}'s {B_BUFF1}!"), //not in gen 5+, generic rapid spin?
[STRINGID_PKMNSHEDLEECHSEED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} shed Leech Seed!"), //not in gen 5+, generic rapid spin?
[STRINGID_PKMNBLEWAWAYSPIKES] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} blew away Spikes!"), //not in gen 5+, generic rapid spin?
@ -628,13 +628,13 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_ICEBODYHPGAIN] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} healed it a little bit!"), //don't think this message is displayed anymore
[STRINGID_SNOWWARNINGHAIL] = COMPOUND_STRING("It started to hail!"),
[STRINGID_FRISKACTIVATES] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} frisked {B_DEF_NAME_WITH_PREFIX2} and found its {B_LAST_ITEM}!"),
[STRINGID_UNNERVEENTERS] = COMPOUND_STRING("{B_SCR_TEAM1} team is too nervous to eat Berries!"),
[STRINGID_UNNERVEENTERS] = COMPOUND_STRING("{B_EFF_TEAM1} team is too nervous to eat Berries!"),
[STRINGID_HARVESTBERRY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} harvested its {B_LAST_ITEM}!"),
[STRINGID_MAGICBOUNCEACTIVATES] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} bounced the {B_ATK_NAME_WITH_PREFIX2} back!"),
[STRINGID_PROTEANTYPECHANGE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} transformed it into the {B_BUFF1} type!"),
[STRINGID_SYMBIOSISITEMPASS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} passed its {B_LAST_ITEM} to {B_EFF_NAME_WITH_PREFIX2} through {B_LAST_ABILITY}!"),
[STRINGID_STEALTHROCKDMG] = COMPOUND_STRING("Pointed stones dug into {B_SCR_NAME_WITH_PREFIX2}!"),
[STRINGID_TOXICSPIKESABSORBED] = COMPOUND_STRING("The poison spikes disappeared from the ground around {B_SCR_TEAM2} team!"),
[STRINGID_TOXICSPIKESABSORBED] = COMPOUND_STRING("The poison spikes disappeared from the ground around {B_EFF_TEAM2} team!"),
[STRINGID_TOXICSPIKESPOISONED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was poisoned!"),
[STRINGID_TOXICSPIKESBADLYPOISONED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was badly poisoned!"),
[STRINGID_STICKYWEBSWITCHIN] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was caught in a sticky web!"),
@ -854,7 +854,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_SWAMPENVELOPEDSIDE] = COMPOUND_STRING("A swamp enveloped {B_DEF_TEAM2} team!"),
[STRINGID_THESWAMPDISAPPEARED] = COMPOUND_STRING("The swamp around {B_ATK_TEAM2} team disappeared!"),
[STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is preparing to tell a chillingly bad joke!"),
[STRINGID_HOSPITALITYRESTORATION] = COMPOUND_STRING("{B_ATK_PARTNER_NAME} drank down all the matcha that {B_ATK_NAME_WITH_PREFIX2} made!"),
[STRINGID_HOSPITALITYRESTORATION] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX} drank down all the matcha that {B_SCR_NAME_WITH_PREFIX2} made!"),
[STRINGID_ELECTROSHOTCHARGING] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} absorbed electricity!"),
[STRINGID_ITEMWASUSEDUP] = COMPOUND_STRING("The {B_LAST_ITEM} was used up…"),
[STRINGID_ATTACKERLOSTITSTYPE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} lost its {B_BUFF1} type!"),
@ -1203,7 +1203,7 @@ const u16 gGotBurnedStringIds[] =
const u16 gGotFrostbiteStringIds[] =
{
[B_MSG_STATUSED] = STRINGID_PKMNGOTFROSTBITE,
[B_MSG_STATUSED_BY_ABILITY] = STRINGID_PKMNGOTFROSTBITE
[B_MSG_STATUSED_BY_ABILITY] = STRINGID_PKMNGOTFROSTBITE,
};
const u16 gFrostbiteHealedStringIds[] =
@ -3121,14 +3121,14 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
else
toCpy = sText_Opposing2;
break;
case B_TXT_SCR_TEAM1:
if (IsOnPlayerSide(gBattleScripting.battler))
case B_TXT_EFF_TEAM1:
if (IsOnPlayerSide(gEffectBattler))
toCpy = sText_Your1;
else
toCpy = sText_Opposing1;
break;
case B_TXT_SCR_TEAM2:
if (IsOnPlayerSide(gBattleScripting.battler))
case B_TXT_EFF_TEAM2:
if (IsOnPlayerSide(gEffectBattler))
toCpy = sText_Your2;
else
toCpy = sText_Opposing2;

View File

@ -930,8 +930,6 @@ static const u16 sProtectSuccessRates[] = {USHRT_MAX, USHRT_MAX / 2, USHRT_MAX /
static const u16 sFinalStrikeOnlyEffects[] =
{
MOVE_EFFECT_BUG_BITE,
MOVE_EFFECT_STEAL_ITEM,
MOVE_EFFECT_REMOVE_ARG_TYPE,
MOVE_EFFECT_REMOVE_STATUS,
MOVE_EFFECT_RECOIL_HP_25,
@ -2448,6 +2446,14 @@ static void Cmd_datahpupdate(void)
&& GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS
&& IsBattlerTurnDamaged(gBattlerTarget))
gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]++;
if (GetMoveEffect(gCurrentMove) == EFFECT_KNOCK_OFF
&& IsBattlerTurnDamaged(gBattlerTarget)
&& gBattleMons[gBattlerTarget].item != 0
&& !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove)
&& CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerTarget].item)
&& !NoAliveMonsForEitherParty())
gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = TRUE;
}
TryRestoreDamageAfterCheekPouch(battler);
@ -2827,7 +2833,7 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem)
if (B_STEAL_WILD_ITEMS >= GEN_9
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))
&& MoveHasAdditionalEffect(gCurrentMove, MOVE_EFFECT_STEAL_ITEM)
&& GetMoveEffect(gCurrentMove) == EFFECT_STEAL_ITEM
&& battlerStealer == gBattlerAttacker) // ensure that Pickpocket isn't activating this
{
AddBagItem(gLastUsedItem, 1);
@ -2994,10 +3000,8 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
switch (gBattleScripting.moveEffect) // Set move effects which happen later on
{
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
case MOVE_EFFECT_PAYDAY:
case MOVE_EFFECT_BUG_BITE:
case MOVE_EFFECT_STEAL_ITEM:
activateAfterFaint = TRUE;
break;
}
@ -3317,35 +3321,6 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
gBattleMons[gBattlerAttacker].volatiles.rage = TRUE;
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_STEAL_ITEM:
if (!CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item)
|| gBattleMons[gBattlerAttacker].item != ITEM_NONE
|| gBattleMons[gBattlerTarget].item == ITEM_NONE)
{
gBattlescriptCurrInstr++;
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD)
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_NoItemSteal;
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
else
{
StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item
if (!(B_STEAL_WILD_ITEMS >= GEN_9
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))))
{
gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS)
gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later
}
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_ItemSteal;
}
break;
case MOVE_EFFECT_PREVENT_ESCAPE:
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
{
@ -3558,18 +3533,6 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
gBattlescriptCurrInstr = BattleScript_StealthRockActivates;
}
break;
case MOVE_EFFECT_SPIKES:
if (gSideTimers[GetBattlerSide(gEffectBattler)].spikesAmount < 3)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
BattleScriptPush(gBattlescriptCurrInstr + 1);
if (gBattleStruct->isSkyBattle)
gBattlescriptCurrInstr++;
else
gBattlescriptCurrInstr = BattleScript_SpikesActivates;
}
break;
case MOVE_EFFECT_SYRUP_BOMB:
if (!(gStatuses4[gEffectBattler] & STATUS4_SYRUP_BOMB))
{
@ -4072,14 +4035,12 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
gBattlescriptCurrInstr = BattleScript_EffectHealBell_FromHeal;
break;
case MOVE_EFFECT_RECYCLE_BERRIES:
{
if (RandomPercentage(RNG_G_MAX_REPLENISH, 50))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_EffectRecycleBerriesAllies;
}
break;
}
case MOVE_EFFECT_REMOVE_STATUS:
{
u32 argStatus = GetMoveEffectArg_Status(gCurrentMove);
@ -5551,45 +5512,6 @@ static void Cmd_unused_0x48(void)
{
}
static bool32 TryKnockOffBattleScript(u32 battlerDef)
{
if (gBattleMons[battlerDef].item != 0
&& CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item)
&& !NoAliveMonsForEitherParty())
{
if (GetBattlerAbility(battlerDef) == ABILITY_STICKY_HOLD && IsBattlerAlive(battlerDef))
{
gBattlerAbility = battlerDef;
BattleScriptCall(BattleScript_StickyHoldActivates);
}
else
{
u32 side = GetBattlerSide(battlerDef);
gLastUsedItem = gBattleMons[battlerDef].item;
gBattleMons[battlerDef].item = 0;
if (gBattleMons[battlerDef].ability != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[battlerDef] = 0;
CheckSetUnburden(battlerDef);
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable.
if (B_KNOCK_OFF_REMOVAL >= GEN_5)
{
BtlController_EmitSetMonData(battlerDef, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battlerDef].item), &gBattleMons[battlerDef].item);
MarkBattlerForControllerExec(battlerDef);
}
else
{
gWishFutureKnock.knockedOffMons[side] |= 1u << gBattlerPartyIndexes[battlerDef];
}
BattleScriptCall(BattleScript_KnockedOff);
}
return TRUE;
}
return FALSE;
}
static inline bool32 TryTriggerSymbiosis(u32 battler, u32 ally)
{
return GetBattlerAbility(ally) == ABILITY_SYMBIOSIS
@ -5796,6 +5718,220 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
return effect;
}
static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
{
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
return FALSE;
u32 effect = FALSE;
switch (moveEffect)
{
case EFFECT_KNOCK_OFF:
if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff && IsBattlerAlive(gBattlerAttacker))
{
u32 side = GetBattlerSide(gBattlerTarget);
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattleMons[gBattlerTarget].item = 0;
if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerTarget] = 0;
CheckSetUnburden(gBattlerTarget);
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable.
if (B_KNOCK_OFF_REMOVAL >= GEN_5)
{
BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
MarkBattlerForControllerExec(gBattlerTarget);
}
else
{
gWishFutureKnock.knockedOffMons[side] |= 1u << gBattlerPartyIndexes[gBattlerTarget];
}
gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = FALSE;
BattleScriptCall(BattleScript_KnockedOff);
effect = TRUE;
}
break;
case EFFECT_STEAL_ITEM:
if (!CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item)
|| gBattleMons[gBattlerAttacker].item != ITEM_NONE
|| gBattleMons[gBattlerTarget].item == ITEM_NONE
|| !IsBattlerAlive(gBattlerAttacker)
|| !IsBattlerTurnDamaged(gBattlerTarget))
{
effect = FALSE;
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD)
{
BattleScriptCall(BattleScript_NoItemSteal);
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
effect = TRUE;
}
else
{
StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item
if (!(B_STEAL_WILD_ITEMS >= GEN_9
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))))
{
gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS)
gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later
}
BattleScriptCall(BattleScript_ItemSteal);
effect = TRUE;
}
break;
case EFFECT_HIT_SWITCH_TARGET:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_COMMANDER))
{
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
if (targetAbility == ABILITY_GUARD_DOG)
return FALSE;
if (targetAbility == ABILITY_SUCTION_CUPS)
{
BattleScriptCall(BattleScript_AbilityPreventsPhasingOutRet);
}
else if (gStatuses3[gBattlerTarget] & STATUS3_ROOTED)
{
BattleScriptCall(BattleScript_PrintMonIsRootedRet);
}
else if (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
{
BattleScriptCall(BattleScript_HitSwitchTargetDynamaxed);
}
else
{
gBattleScripting.switchCase = B_SWITCH_HIT;
BattleScriptCall(BattleScript_TryHitSwitchTarget);
}
effect = TRUE;
}
break;
case EFFECT_SMACK_DOWN:
if (!IsBattlerGrounded(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
gStatuses3[gBattlerTarget] |= STATUS3_SMACKED_DOWN;
gStatuses3[gBattlerTarget] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR);
BattleScriptCall(BattleScript_MoveEffectSmackDown);
effect = TRUE;
}
break;
case EFFECT_RECOIL_IF_MISS:
if (IsBattlerAlive(gBattlerAttacker)
&& (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !gBattleStruct->noTargetPresent)
{
if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_DOESNT_AFFECT_FOE))
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (GetNonDynamaxMaxHP(gBattlerTarget) / 2) < gBattleStruct->moveDamage[gBattlerTarget])
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
else // Fallback if B_RECOIL_IF_MISS_DMG is set to gen3 or lower.
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
BattleScriptCall(BattleScript_RecoilIfMiss);
effect = TRUE;
}
break;
case EFFECT_RECOIL:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleScripting.savedDmg * max(1, GetMoveRecoil(gCurrentMove)) / 100);
BattleScriptCall(BattleScript_MoveEffectRecoil);
effect = TRUE;
}
break;
case EFFECT_EXPLOSION:
if (!IsAbilityOnField(ABILITY_DAMP))
{
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
BattleScriptCall(BattleScript_FaintAttackerForExplosion);
effect = TRUE;
}
break;
case EFFECT_MAX_HP_50_RECOIL:
if (IsBattlerAlive(gBattlerAttacker)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptCall(BattleScript_MaxHp50Recoil);
effect = TRUE;
}
break;
case EFFECT_CHLOROBLAST:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptCall(BattleScript_MoveEffectRecoil);
effect = TRUE;
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
{
BattleScriptCall(BattleScript_RapidSpinAway);
effect = TRUE;
}
break;
case EFFECT_FELL_STINGER:
if (IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat;
effect = TRUE;
}
break;
case EFFECT_STONE_AXE:
if (!IsHazardOnSide(GetBattlerSide(gBattlerTarget), HAZARDS_STEALTH_ROCK) && IsBattlerAlive(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_StealthRockActivates;
effect = TRUE;
}
break;
case EFFECT_CEASELESS_EDGE:
if (gSideTimers[GetBattlerSide(gBattlerTarget)].spikesAmount < 3 && IsBattlerAlive(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
BattleScriptPush(gBattlescriptCurrInstr + 1);
if (gBattleStruct->isSkyBattle)
{
effect = FALSE;
}
else
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SpikesActivates;
effect = TRUE;
}
}
break;
default:
effect = FALSE;
break;
}
return effect;
}
static void Cmd_moveend(void)
{
CMD_ARGS(u8 endMode, u8 endState);
@ -6065,71 +6201,6 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_FIRST_MOVE_BLOCK:
if ((gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT && IsBattlerAlive(gBattlerTarget))
|| gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT
|| gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
{
gBattleScripting.moveendState++;
break;
}
switch (moveEffect)
{
case EFFECT_KNOCK_OFF:
if (!DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
effect = TryKnockOffBattleScript(gBattlerTarget);
break;
case EFFECT_HIT_SWITCH_TARGET:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_COMMANDER))
{
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
if (targetAbility == ABILITY_GUARD_DOG)
{
gBattleScripting.moveendState++;
break;
}
effect = TRUE;
if (targetAbility == ABILITY_SUCTION_CUPS)
{
BattleScriptCall(BattleScript_AbilityPreventsPhasingOutRet);
}
else if (gStatuses3[gBattlerTarget] & STATUS3_ROOTED)
{
BattleScriptCall(BattleScript_PrintMonIsRootedRet);
}
else if (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
{
BattleScriptCall(BattleScript_HitSwitchTargetDynamaxed);
}
else
{
gBattleScripting.switchCase = B_SWITCH_HIT;
BattleScriptCall(BattleScript_TryHitSwitchTarget);
}
}
break;
case EFFECT_SMACK_DOWN:
if (!IsBattlerGrounded(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
gStatuses3[gBattlerTarget] |= STATUS3_SMACKED_DOWN;
gStatuses3[gBattlerTarget] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR);
effect = TRUE;
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_MoveEffectSmackDown;
}
break;
default:
break;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_ALL: // item effects for all battlers
if (ItemBattleEffects(ITEMEFFECT_MOVE_END, 0))
effect = TRUE;
@ -6464,90 +6535,8 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
}
case MOVEEND_SECOND_MOVE_BLOCK:
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
{
gBattleScripting.moveendState++;
break;
}
switch (moveEffect)
{
case EFFECT_RECOIL_IF_MISS:
if (IsBattlerAlive(gBattlerAttacker)
&& (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !gBattleStruct->noTargetPresent)
{
if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_DOESNT_AFFECT_FOE))
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (GetNonDynamaxMaxHP(gBattlerTarget) / 2) < gBattleStruct->moveDamage[gBattlerTarget])
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
else // Fallback if B_RECOIL_IF_MISS_DMG is set to gen3 or lower.
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
BattleScriptCall(BattleScript_RecoilIfMiss);
effect = TRUE;
}
break;
case EFFECT_RECOIL:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleStruct->moveDamage[gBattlerTarget] * max(1, GetMoveRecoil(gCurrentMove)) / 100);
BattleScriptCall(BattleScript_MoveEffectRecoil);
effect = TRUE;
}
break;
case EFFECT_EXPLOSION:
case EFFECT_MISTY_EXPLOSION:
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
BattleScriptCall(BattleScript_FaintAttackerForExplosion);
effect = TRUE;
break;
case EFFECT_MAX_HP_50_RECOIL:
if (IsBattlerAlive(gBattlerAttacker)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptCall(BattleScript_MaxHp50Recoil);
effect = TRUE;
}
break;
case EFFECT_CHLOROBLAST:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
effect = TRUE;
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
{
BattleScriptCall(BattleScript_RapidSpinAway);
effect = TRUE;
}
break;
case EFFECT_FELL_STINGER:
if (IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
BattleScriptCall(BattleScript_FellStingerRaisesStat);
effect = TRUE;
}
break;
default:
break;
}
case MOVEEND_MOVE_BLOCK:
effect = HandleMoveEndMoveBlock(moveEffect);
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_ATTACKER:
@ -6866,7 +6855,9 @@ static void Cmd_moveend(void)
switch (moveEffect)
{
case EFFECT_ICE_SPINNER:
if (IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget))
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY
&& IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget))
{
BattleScriptCall(BattleScript_RemoveTerrain);
effect = TRUE;
@ -7773,7 +7764,7 @@ void TryHazardsOnSwitchIn(u32 battler, u32 side, enum Hazards hazardType)
gBattleStruct->hazardsCounter--; // reduce counter so the next hazard can be applied
gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount = 0;
RemoveHazardFromField(side, HAZARDS_TOXIC_SPIKES);
gBattleScripting.battler = battler;
gEffectBattler = battler;
BattleScriptCall(BattleScript_ToxicSpikesAbsorbed);
}
else if (IsBattlerAffectedByHazards(battler, TRUE)
@ -15828,9 +15819,14 @@ void BS_TryGulpMissile(void)
&& (gCurrentMove == MOVE_DIVE)
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_GULP_MISSILE
&& TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT))
{
gBattleScripting.battler = gBattlerAttacker;
gBattlescriptCurrInstr = BattleScript_GulpMissileFormChange;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TryActivateGulpMissile(void)

View File

@ -840,10 +840,6 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3)
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(move, i);
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_STEAL_ITEM:
if ((additionalEffect->chance == 100 || additionalEffect->chance == 0))
baseFromEffect += 3;
break;
case MOVE_EFFECT_THRASH:
if (additionalEffect->self == TRUE)
baseFromEffect += 3;

View File

@ -1864,7 +1864,8 @@ static inline bool32 TryFormChangeBeforeMove(void)
if (!result)
return FALSE;
BattleScriptCall(BattleScript_AttackerFormChange);
gBattleScripting.battler = gBattlerAttacker;
BattleScriptCall(BattleScript_BattlerFormChange);
return TRUE;
}
@ -3603,7 +3604,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_UNNERVE:
if (!gSpecialStatuses[battler].switchInAbilityDone && !gDisableStructs[battler].unnerveActivated)
{
gBattleScripting.battler = GetOppositeBattler(battler);
gEffectBattler = GetOppositeBattler(battler);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_UNNERVE;
gDisableStructs[battler].unnerveActivated = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -3615,7 +3616,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_AS_ONE_SHADOW_RIDER:
if (!gSpecialStatuses[battler].switchInAbilityDone && !gDisableStructs[battler].unnerveActivated)
{
gBattleScripting.battler = GetOppositeBattler(battler);
gEffectBattler = GetOppositeBattler(battler);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ASONE;
gDisableStructs[battler].unnerveActivated = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -3729,6 +3730,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
{
SET_STATCHANGER(statId, 1, FALSE);
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3);
effect++;
@ -3878,7 +3881,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_INTIMIDATE:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
gBattlerAbility = gBattlerAttacker = battler;
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(STAT_ATK, 1, TRUE);
BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivates);
@ -3889,7 +3893,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!gSpecialStatuses[battler].switchInAbilityDone
&& !gBattleStruct->partyState[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].supersweetSyrup)
{
gBattlerAbility = gBattlerAttacker = battler;
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleStruct->partyState[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].supersweetSyrup = TRUE;
BattleScriptPushCursorAndCallback(BattleScript_SupersweetSyrupActivates);
@ -3921,7 +3926,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_SHIELDS_DOWN:
if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT))
{
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3);
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeEnd3);
effect++;
}
break;
@ -4043,10 +4048,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& CountBattlerStatIncreases(BATTLE_PARTNER(battler), FALSE))
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattlerAttacker = gBattlerAbility = battler;
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[battler].statStages[i] = gBattleMons[BATTLE_PARTNER(battler)].statStages[i];
gBattleScripting.battler = BATTLE_PARTNER(battler);
gEffectBattler = BATTLE_PARTNER(battler);
BattleScriptPushCursorAndCallback(BattleScript_CostarActivates);
effect++;
}
@ -4074,10 +4078,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& gBattleMons[partner].hp < gBattleMons[partner].maxHP
&& IsBattlerAlive(partner))
{
SaveBattlerAttacker(gBattlerAttacker);
SaveBattlerTarget(gBattlerTarget);
gBattlerTarget = partner;
gBattlerAttacker = battler;
gEffectBattler = partner;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleStruct->moveDamage[partner] = (GetNonDynamaxMaxHP(partner) / 4) * -1;
BattleScriptPushCursorAndCallback(BattleScript_HospitalityActivates);
@ -4117,7 +4118,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
{
gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeWithStringEnd3);
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3);
effect++;
}
break;
@ -4336,7 +4337,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_SHIELDS_DOWN:
if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT))
{
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3);
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeEnd3);
effect++;
}
break;
@ -4368,7 +4370,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
{
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup);
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeEnd3NoPopup);
effect++;
}
break;
@ -4393,6 +4395,16 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITYEFFECT_MOVE_END: // Think contact abilities.
switch (gLastUsedAbility)
{
case ABILITY_STICKY_HOLD:
if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff && IsBattlerAlive(gBattlerTarget))
{
gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = FALSE;
gBattlerAbility = gBattlerTarget;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_StickyHoldActivates;
effect++;
}
break;
case ABILITY_JUSTIFIED:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
@ -4950,7 +4962,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& ((gCurrentMove == MOVE_SURF && IsBattlerTurnDamaged(gBattlerTarget)) || gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER)
&& TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT))
{
BattleScriptCall(BattleScript_AttackerFormChange);
gBattleScripting.battler = gBattlerAttacker;
BattleScriptCall(BattleScript_BattlerFormChange);
effect++;
}
break;
@ -6939,6 +6952,12 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case ITEMEFFECT_MOVE_END:
for (battler = 0; battler < gBattlersCount; battler++)
{
// If item can be knocked off berry activation will be blocked, but not other items
if (gBattleStruct->battlerState[battler].itemCanBeKnockedOff
&& (GetItemPocket(gBattleMons[battler].item) == POCKET_BERRIES
|| GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RESTORE_HP))
continue;
gLastUsedItem = gBattleMons[battler].item;
effect = ItemEffectMoveEnd(battler, GetBattlerHoldEffect(battler, TRUE));
if (effect)

View File

@ -1004,6 +1004,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleTvScore = 2,
},
[EFFECT_STEAL_ITEM] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 3,
},
[EFFECT_ENDEAVOR] =
{
.battleScript = BattleScript_EffectEndeavor,
@ -2230,4 +2236,16 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectSteelRoller,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_STONE_AXE] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_CEASELESS_EDGE] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
};

View File

@ -4484,7 +4484,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"While attacking, it may\n"
"steal the foe's held item."),
.effect = EFFECT_HIT,
.effect = EFFECT_STEAL_ITEM,
.power = B_UPDATED_MOVE_DATA >= GEN_6 ? 60 : 40,
.type = TYPE_DARK,
.accuracy = 100,
@ -4493,9 +4493,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
.makesContact = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_STEAL_ITEM,
}),
.ignoresKingsRock = (B_UPDATED_MOVE_FLAGS == GEN_3 || B_UPDATED_MOVE_FLAGS == GEN_4),
.meFirstBanned = TRUE,
.metronomeBanned = TRUE,
@ -9043,7 +9040,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"Cutely begs to obtain an\n"
"item held by the foe."),
.effect = EFFECT_HIT,
.effect = EFFECT_STEAL_ITEM,
.power = B_UPDATED_MOVE_DATA >= GEN_5 ? 60 : 40,
.type = TYPE_NORMAL,
.accuracy = 100,
@ -9056,9 +9053,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.metronomeBanned = TRUE,
.copycatBanned = TRUE,
.assistBanned = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_STEAL_ITEM,
}),
.contestEffect = CONTEST_EFFECT_APPEAL_AS_GOOD_AS_PREV_ONES,
.contestCategory = CONTEST_CATEGORY_CUTE,
.contestComboStarterId = 0,
@ -19197,7 +19191,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"High critical hit ratio. Sets\n"
"Splinters that hurt the foe."),
.effect = EFFECT_HIT,
.effect = EFFECT_STONE_AXE,
.power = 65,
.type = TYPE_ROCK,
.accuracy = 90,
@ -19208,8 +19202,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.makesContact = TRUE,
.slicingMove = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_STEALTH_ROCK,
.chance = 100,
.sheerForceBoost = SHEER_FORCE_BOOST,
}),
.battleAnimScript = gBattleAnimMove_StoneAxe,
},
@ -19523,7 +19516,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"High critical hit ratio. Sets\n"
"Splinters that hurt the foe."),
.effect = EFFECT_HIT,
.effect = EFFECT_CEASELESS_EDGE,
.power = 65,
.type = TYPE_DARK,
.accuracy = 90,
@ -19534,8 +19527,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.makesContact = TRUE,
.slicingMove = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_SPIKES,
.chance = 100,
.sheerForceBoost = SHEER_FORCE_BOOST,
}),
.battleAnimScript = gBattleAnimMove_CeaselessEdge,
},

View File

@ -1,7 +1,6 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(MoveIsAffectedBySheerForce(MOVE_ELECTRO_SHOT) == TRUE);

View File

@ -4,7 +4,7 @@
SINGLE_BATTLE_TEST("Sticky Hold prevents item theft")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM));
ASSUME(GetMoveEffect(MOVE_THIEF) == EFFECT_STEAL_ITEM);
PLAYER(SPECIES_URSALUNA) { Item(ITEM_NONE); }
OPPONENT(SPECIES_GASTRODON) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_LIFE_ORB); }
} WHEN {

View File

@ -882,6 +882,19 @@ AI_SINGLE_BATTLE_TEST("Move scoring comparison properly awards bonus point to be
}
}
AI_SINGLE_BATTLE_TEST("AI will stop setting up at +4")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE, MOVE_IRON_DEFENSE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_MOVE(opponent, MOVE_IRON_DEFENSE); }
TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_MOVE(opponent, MOVE_IRON_DEFENSE); }
TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_MOVE(opponent, MOVE_TACKLE); }
}
}
AI_SINGLE_BATTLE_TEST("Move scoring comparison properly awards bonus point to best OHKO move")
{
GIVEN {

View File

@ -102,17 +102,14 @@ SINGLE_BATTLE_TEST("Air Balloon pops before it can be stolen with Magician")
}
}
SINGLE_BATTLE_TEST("Air Balloon pops before it can be stolen with Thief or Covet")
SINGLE_BATTLE_TEST("Air Balloon pops before it can be stolen by Thief")
{
u32 move;
PARAMETRIZE { move = MOVE_THIEF; }
PARAMETRIZE { move = MOVE_COVET; }
GIVEN {
ASSUME(MoveHasAdditionalEffect(move, MOVE_EFFECT_STEAL_ITEM) == TRUE);
ASSUME(GetMoveEffect(MOVE_THIEF) == EFFECT_STEAL_ITEM);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_AIR_BALLOON); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); }
TURN { MOVE(opponent, MOVE_THIEF); }
} SCENE {
MESSAGE("Wobbuffet floats in the air with its Air Balloon!");
MESSAGE("Wobbuffet's Air Balloon popped!");

View File

@ -170,7 +170,7 @@ SINGLE_BATTLE_TEST("Red Card does not activate if stolen by a move")
bool32 activate;
PARAMETRIZE { item = ITEM_NONE; activate = FALSE; }
PARAMETRIZE { item = ITEM_POTION; activate = TRUE; }
ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM) == TRUE);
ASSUME(GetMoveEffect(MOVE_THIEF) == EFFECT_STEAL_ITEM);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }

View File

@ -132,23 +132,22 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if it is knocked off o
PARAMETRIZE { move = MOVE_KNOCK_OFF; }
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM) == TRUE);
ASSUME(GetMoveEffect(MOVE_KNOCK_OFF) == EFFECT_KNOCK_OFF);
ASSUME(GetMoveEffect(MOVE_THIEF) == EFFECT_STEAL_ITEM);
PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (move == MOVE_THIEF) {
MESSAGE("The opposing Wobbuffet stole Slugma's White Herb!");
}
ABILITY_POPUP(player, ABILITY_WEAK_ARMOR);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Slugma's Weak Armor lowered its Defense!");
MESSAGE("Slugma's Weak Armor raised its Speed!");
if (move == MOVE_KNOCK_OFF) {
MESSAGE("The opposing Wobbuffet knocked off Slugma's White Herb!");
} else if (move == MOVE_THIEF) {
MESSAGE("The opposing Wobbuffet stole Slugma's White Herb!");
}
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_CEASELESS_EDGE, MOVE_EFFECT_SPIKES) == TRUE);
ASSUME(GetMoveEffect(MOVE_CEASELESS_EDGE) == EFFECT_CEASELESS_EDGE);
}
SINGLE_BATTLE_TEST("Ceaseless Edge sets up hazards after hitting the target")
@ -62,3 +62,19 @@ SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes")
MESSAGE("The opposing Wynaut was hurt by the spikes!");
}
}
SINGLE_BATTLE_TEST("Ceaseless Edge fails to set up hazards if user faints")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); }
} WHEN {
TURN { MOVE(player, MOVE_CEASELESS_EDGE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(player);
MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Rocky Helmet!");
NOT MESSAGE("Spikes were scattered on the ground all around the opposing team!");
}
}

View File

@ -43,7 +43,7 @@ DOUBLE_BATTLE_TEST("Dragon Tail switches the target with a random non-battler, n
}
}
SINGLE_BATTLE_TEST("Dragon Tail does not fail if no replacements")
SINGLE_BATTLE_TEST("Dragon Tail fails if no replacements")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Dragon Tail does not fail if no replacements")
}
}
SINGLE_BATTLE_TEST("Dragon Tail does not fail if replacements fainted")
SINGLE_BATTLE_TEST("Dragon Tail fails if replacements fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);

View File

@ -96,7 +96,10 @@ SINGLE_BATTLE_TEST("Ice Spinner doesn't fail if there is no terrain on the field
TURN { MOVE(player, MOVE_ICE_SPINNER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_SPINNER, player);
NOT MESSAGE("But it failed!");
NONE_OF {
MESSAGE("But it failed!");
MESSAGE("Mist swirled around the battlefield!");
}
}
}

View File

@ -99,6 +99,22 @@ SINGLE_BATTLE_TEST("Knock Off does not remove items through Substitute")
}
}
SINGLE_BATTLE_TEST("Knock Off does not remove items through Substitute even if it breaks it")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(4); HP(4); Item(ITEM_LEFTOVERS); };
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
MESSAGE("The opposing Wobbuffet's substitute faded!");
NOT { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); }
} THEN {
EXPECT(opponent->item == ITEM_LEFTOVERS);
}
}
SINGLE_BATTLE_TEST("Knock Off does not remove items through Protect")
{
GIVEN {
@ -229,18 +245,6 @@ DOUBLE_BATTLE_TEST("Knock Off does not trigger the opposing ally's Symbiosis")
}
}
SINGLE_BATTLE_TEST("Knock Off doesn't knock off items from Pokemon behind substitutes")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_POKE_BALL); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
NOT MESSAGE("Wobbuffet knocked off the opposing Wobbuffet's Poké Ball!");
}
}
SINGLE_BATTLE_TEST("Knock Off does knock off Mega Stones from Pokemon that don't actually use them")
{
GIVEN {
@ -360,3 +364,33 @@ SINGLE_BATTLE_TEST("Knock Off doesn't knock off begin-battle form-change hold it
NOT MESSAGE("Wobbuffet knocked off the opposing Zamazenta's Rusted Shield!");
}
}
SINGLE_BATTLE_TEST("Knock Off does not activate if user faints")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); }
} WHEN {
TURN { MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Rocky Helmet!");
MESSAGE("Wobbuffet fainted!");
} THEN {
EXPECT(opponent->item == ITEM_ROCKY_HELMET);
}
}
SINGLE_BATTLE_TEST("Knock Off doesn't remove item if it's prevented by Sticky Hold")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_MUK) { MaxHP(100); HP(51); Item(ITEM_ORAN_BERRY); Ability(ABILITY_STICKY_HOLD); }
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
}
}

View File

@ -0,0 +1,94 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_STONE_AXE) == EFFECT_STONE_AXE);
}
SINGLE_BATTLE_TEST("Stone Axe sets up hazards after hitting the target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wobbuffet!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into the opposing Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into the opposing Wynaut!");
}
}
SINGLE_BATTLE_TEST("Stone Axe sets up hazards after any ability activation")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_SKARMORY) { Ability(ABILITY_WEAK_ARMOR); }
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
ABILITY_POPUP(opponent, ABILITY_WEAK_ARMOR);
MESSAGE("Pointed stones float in the air around the opposing team!");
}
}
SINGLE_BATTLE_TEST("Stone Axe fails to set up hazards if user faints")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); }
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(player);
MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Rocky Helmet!");
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
}
}

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_PAY_DAY, MOVE_EFFECT_PAYDAY));
ASSUME(MoveHasAdditionalEffectWithChance(MOVE_PAY_DAY, MOVE_EFFECT_PAYDAY, 0) == TRUE);
}
SINGLE_BATTLE_TEST("Pay Day Scatters coins around after it hits - singles")

View File

@ -3,8 +3,8 @@
ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_COVET, MOVE_EFFECT_STEAL_ITEM) == TRUE);
ASSUME(GetMoveEffect(MOVE_THIEF == EFFECT_STEAL_ITEM));
ASSUME(GetMoveEffect(MOVE_COVET == EFFECT_STEAL_ITEM));
}
SINGLE_BATTLE_TEST("Thief and Covet steal target's held item")
@ -127,3 +127,41 @@ WILD_BATTLE_TEST("Thief and Covet steal target's held item and it's added to Bag
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Thief and Covet can't steal target's held item if user faints before")
{
u32 move;
PARAMETRIZE { move = MOVE_THIEF; }
PARAMETRIZE { move = MOVE_COVET; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); };
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent);
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
EXPECT_EQ(opponent->item, ITEM_ROCKY_HELMET);
}
}
SINGLE_BATTLE_TEST("Thief and Covet: Berry activation happens before the item can be stolen")
{
u32 move;
PARAMETRIZE { move = MOVE_THIEF; }
PARAMETRIZE { move = MOVE_COVET; }
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(200); HP(101); Item(ITEM_ORAN_BERRY); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent);
}
}

View File

@ -3,63 +3,19 @@
ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_STONE_AXE, MOVE_EFFECT_STEALTH_ROCK) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_STONESURGE, MOVE_EFFECT_STEALTH_ROCK));
}
SINGLE_BATTLE_TEST("Stone Axe sets up hazards after hitting the target")
SINGLE_BATTLE_TEST("Steath Rock: Rock from G-Max Stonesurge are set up before any ability activation")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_DREDNAW) { GigantamaxFactor(TRUE); }
OPPONENT(SPECIES_SKARMORY) { Ability(ABILITY_WEAK_ARMOR); }
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
TURN { MOVE(player, MOVE_WATERFALL, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_STONESURGE, player);
MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wobbuffet!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into the opposing Wobbuffet!");
ABILITY_POPUP(opponent, ABILITY_WEAK_ARMOR);
}
}
SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into the opposing Wynaut!");
}
}

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_MAKE_IT_RAIN, MOVE_EFFECT_PAYDAY));
ASSUME(MoveHasAdditionalEffectWithChance(MOVE_MAKE_IT_RAIN, MOVE_EFFECT_PAYDAY, 0) == TRUE);
ASSUME(MoveHasAdditionalEffectSelf(MOVE_MAKE_IT_RAIN, MOVE_EFFECT_SP_ATK_MINUS_1));
}

View File

@ -102,3 +102,23 @@ SINGLE_BATTLE_TEST("Recoil: Flare Blitz is absorbed by Flash Fire and no recoil
}
}
}
SINGLE_BATTLE_TEST("Recoil: The correct amount of recoil damage is dealt after targets recovery berry proc")
{
s16 directDamage;
s16 recoilDamage;
GIVEN {
ASSUME(GetMoveRecoil(MOVE_TAKE_DOWN) == 25);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); };
} WHEN {
TURN { MOVE(player, MOVE_TAKE_DOWN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
HP_BAR(opponent, captureDamage: &directDamage);
HP_BAR(player, captureDamage: &recoilDamage);
} THEN {
EXPECT_MUL_EQ(directDamage, UQ_4_12(0.25), recoilDamage);
}
}