mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-04-26 10:25:49 -05:00
Refactor Fury Cutter and Rollout (#8375)
This commit is contained in:
parent
a285482310
commit
49a18df48a
|
|
@ -980,7 +980,7 @@
|
|||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
.macro handlerollout
|
||||
.macro unused_0xb3
|
||||
.byte 0xb3
|
||||
.endm
|
||||
|
||||
|
|
@ -990,7 +990,7 @@
|
|||
.4byte \jumpInstr
|
||||
.endm
|
||||
|
||||
.macro handlefurycutter
|
||||
.macro unused_0xb5
|
||||
.byte 0xb5
|
||||
.endm
|
||||
|
||||
|
|
|
|||
|
|
@ -3459,16 +3459,6 @@ BattleScript_EffectSandstorm::
|
|||
setfieldweather BATTLE_WEATHER_SANDSTORM
|
||||
goto BattleScript_MoveWeatherChange
|
||||
|
||||
BattleScript_EffectRollout::
|
||||
attackcanceler
|
||||
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_RolloutCheckAccuracy
|
||||
BattleScript_RolloutCheckAccuracy::
|
||||
accuracycheck BattleScript_RolloutHit, ACC_CURR_MOVE
|
||||
BattleScript_RolloutHit::
|
||||
typecalc
|
||||
handlerollout
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectSwagger::
|
||||
attackcanceler
|
||||
jumpifsubstituteblocks BattleScript_MakeMoveMissed
|
||||
|
|
@ -3487,16 +3477,6 @@ BattleScript_SwaggerTryConfuse:
|
|||
seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_CONFUSION
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_EffectFuryCutter::
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_FuryCutterHit, ACC_CURR_MOVE
|
||||
BattleScript_FuryCutterHit:
|
||||
handlefurycutter
|
||||
damagecalc
|
||||
jumpifmovehadnoeffect BattleScript_FuryCutterHit
|
||||
adjustdamage
|
||||
goto BattleScript_HitFromAtkAnimation
|
||||
|
||||
BattleScript_TryDestinyKnotTarget:
|
||||
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotTargetRet
|
||||
playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT
|
||||
|
|
|
|||
|
|
@ -95,10 +95,10 @@ struct DisableStruct
|
|||
u16 disableTimer;
|
||||
u16 encoreTimer;
|
||||
u16 perishSongTimer;
|
||||
u16 rolloutTimer;
|
||||
u16 rolloutTimerStartValue;
|
||||
u8 rolloutTimer;
|
||||
u16 tauntTimer;
|
||||
u8 furyCutterCounter;
|
||||
u8 metronomeItemCounter;
|
||||
u8 battlerPreventingEscape;
|
||||
u8 battlerWithSureHit;
|
||||
u8 isFirstTurn;
|
||||
|
|
@ -586,10 +586,11 @@ struct BattlerState
|
|||
u32 wasAboveHalfHp:1; // For Berserk, Emergency Exit, Wimp Out and Anger Shell.
|
||||
u32 commanderSpecies:11;
|
||||
u32 selectionScriptFinished:1;
|
||||
u32 switchIn:1;
|
||||
u32 padding:3;
|
||||
u32 lastMoveTarget:3; // The last target on which each mon used a move, for the sake of Instruct
|
||||
// End of Word
|
||||
u16 hpOnSwitchout;
|
||||
u16 switchIn:1;
|
||||
u16 padding:15;
|
||||
};
|
||||
|
||||
struct PartyState
|
||||
|
|
@ -701,9 +702,9 @@ struct BattleStruct
|
|||
struct BattleTvMovePoints tvMovePoints;
|
||||
struct BattleTv tv;
|
||||
u8 AI_monToSwitchIntoId[MAX_BATTLERS_COUNT];
|
||||
s8 arenaMindPoints[2];
|
||||
s8 arenaSkillPoints[2];
|
||||
u16 arenaStartHp[2];
|
||||
s8 arenaMindPoints[NUM_BATTLE_SIDES];
|
||||
s8 arenaSkillPoints[NUM_BATTLE_SIDES];
|
||||
u16 arenaStartHp[NUM_BATTLE_SIDES];
|
||||
u8 arenaLostPlayerMons; // Bits for party member, lost as in referee's decision, not by fainting.
|
||||
u8 arenaLostOpponentMons;
|
||||
u8 debugBattler;
|
||||
|
|
@ -719,12 +720,10 @@ struct BattleStruct
|
|||
struct BattleGimmickData gimmick;
|
||||
const u8 *trainerSlideMsg;
|
||||
u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages
|
||||
u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct
|
||||
enum Ability tracedAbility[MAX_BATTLERS_COUNT];
|
||||
struct Illusion illusion[MAX_BATTLERS_COUNT];
|
||||
u8 soulheartBattlerId;
|
||||
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
|
||||
u8 metronomeItemCounter[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used.
|
||||
u8 quickClawBattlerId;
|
||||
struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side)
|
||||
u8 blunderPolicy:1; // should blunder policy activate
|
||||
|
|
|
|||
|
|
@ -627,9 +627,7 @@ extern const u8 BattleScript_EffectForesight[];
|
|||
extern const u8 BattleScript_EffectPerishSong[];
|
||||
extern const u8 BattleScript_EffectSandstorm[];
|
||||
extern const u8 BattleScript_EffectEndure[];
|
||||
extern const u8 BattleScript_EffectRollout[];
|
||||
extern const u8 BattleScript_EffectSwagger[];
|
||||
extern const u8 BattleScript_EffectFuryCutter[];
|
||||
extern const u8 BattleScript_EffectAttract[];
|
||||
extern const u8 BattleScript_EffectPresent[];
|
||||
extern const u8 BattleScript_EffectSafeguard[];
|
||||
|
|
|
|||
|
|
@ -301,8 +301,6 @@ bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityA
|
|||
bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect);
|
||||
u32 GetMoveSlot(u16 *moves, u32 move);
|
||||
u32 GetBattlerWeight(u32 battler);
|
||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||
s32 CalcCritChanceStage(struct DamageContext *ctx);
|
||||
s32 CalcCritChanceStageGen1(struct DamageContext *ctx);
|
||||
s32 CalculateMoveDamage(struct DamageContext *ctx);
|
||||
|
|
|
|||
|
|
@ -172,7 +172,6 @@ enum MoveEndEffects
|
|||
MOVEEND_PICKPOCKET,
|
||||
MOVEEND_THIRD_MOVE_BLOCK,
|
||||
MOVEEND_CHANGED_ITEMS,
|
||||
MOVEEND_SAME_MOVE_TURNS,
|
||||
MOVEEND_CLEAR_BITS,
|
||||
MOVEEND_DANCER,
|
||||
MOVEEND_PURSUIT_NEXT_ACTION,
|
||||
|
|
|
|||
|
|
@ -744,25 +744,6 @@ static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType)
|
|||
return DmgRoll(dmg);
|
||||
}
|
||||
|
||||
static inline s32 SetFixedMoveBasePower(u32 battlerAtk, u32 move)
|
||||
{
|
||||
s32 fixedBasePower = 0, n = 0;
|
||||
switch (GetMoveEffect(move))
|
||||
{
|
||||
case EFFECT_ROLLOUT:
|
||||
n = gDisableStructs[battlerAtk].rolloutTimer - 1;
|
||||
fixedBasePower = CalcRolloutBasePower(battlerAtk, GetMovePower(move), n < 0 ? 5 : n);
|
||||
break;
|
||||
case EFFECT_FURY_CUTTER:
|
||||
fixedBasePower = CalcFuryCutterBasePower(GetMovePower(move), min(gDisableStructs[battlerAtk].furyCutterCounter + 1, 5));
|
||||
break;
|
||||
default:
|
||||
fixedBasePower = 0;
|
||||
break;
|
||||
}
|
||||
return fixedBasePower;
|
||||
}
|
||||
|
||||
static inline void AI_StoreBattlerTypes(u32 battlerAtk, enum Type *types)
|
||||
{
|
||||
types[0] = gBattleMons[battlerAtk].types[0];
|
||||
|
|
@ -937,7 +918,6 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
{
|
||||
struct SimulatedDamage simDamage = {0};
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
bool32 isDamageMoveUnusable = FALSE;
|
||||
bool32 toggledGimmickAtk = FALSE;
|
||||
bool32 toggledGimmickDef = FALSE;
|
||||
struct AiLogicData *aiData = gAiLogicData;
|
||||
|
|
@ -978,7 +958,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
ctx.randomFactor = FALSE;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.weather = weather;
|
||||
ctx.fixedBasePower = SetFixedMoveBasePower(battlerAtk, move);
|
||||
ctx.fixedBasePower = 0;
|
||||
ctx.holdEffectAtk = aiData->holdEffects[battlerAtk];
|
||||
ctx.holdEffectDef = aiData->holdEffects[battlerDef];
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
|
|
@ -987,10 +967,8 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
u32 movePower = GetMovePower(move);
|
||||
if (movePower)
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(&ctx);
|
||||
|
||||
if (movePower && !isDamageMoveUnusable)
|
||||
if (movePower && !IsDamageMoveUnusable(&ctx))
|
||||
{
|
||||
enum Type types[3];
|
||||
AI_StoreBattlerTypes(battlerAtk, types);
|
||||
|
|
|
|||
|
|
@ -1599,8 +1599,8 @@ static void AnimHailBegin(struct Sprite *sprite)
|
|||
sprite->data[0] = spriteId;
|
||||
if (spriteId != MAX_SPRITES)
|
||||
{
|
||||
// The sprite template we're using is shared amongst a few other
|
||||
// places, which make the sprite flicker. That's not what we want
|
||||
// The sprite template we're using is shared amongst a few other
|
||||
// places, which make the sprite flicker. That's not what we want
|
||||
// here, though. Override the callback.
|
||||
gSprites[sprite->data[0]].callback = AnimHailContinue;
|
||||
gSprites[sprite->data[0]].sOwnerTaskId = sprite->sOwnerTaskId;
|
||||
|
|
@ -1649,10 +1649,7 @@ static void AnimHailContinue(struct Sprite *sprite)
|
|||
// arg 5: arc height (negative)
|
||||
static void InitIceBallAnim(struct Sprite *sprite)
|
||||
{
|
||||
u8 animNum = gAnimDisableStructPtr->rolloutTimerStartValue - gAnimDisableStructPtr->rolloutTimer - 1;
|
||||
|
||||
if (animNum > 4)
|
||||
animNum = 4;
|
||||
u32 animNum = gAnimDisableStructPtr->rolloutTimer + 1;
|
||||
|
||||
StartSpriteAffineAnim(sprite, animNum);
|
||||
InitSpritePosToAnimAttacker(sprite, TRUE);
|
||||
|
|
@ -1721,8 +1718,7 @@ static void AnimIceBallParticle(struct Sprite *sprite)
|
|||
void AnimTask_GetIceBallCounter(u8 taskId)
|
||||
{
|
||||
u8 arg = gBattleAnimArgs[0];
|
||||
|
||||
gBattleAnimArgs[arg] = gAnimDisableStructPtr->rolloutTimerStartValue - gAnimDisableStructPtr->rolloutTimer - 1;
|
||||
gBattleAnimArgs[arg] = gAnimDisableStructPtr->rolloutTimer + 1;
|
||||
DestroyAnimVisualTask(taskId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -626,10 +626,10 @@ static void AnimTask_LoadSandstormBackground_Step(u8 taskId)
|
|||
#define sFractionalY data[4] // 256ths of a pixel
|
||||
#define sMirroredX data[5] // init'd from gBattleAnimArgs[3]
|
||||
|
||||
// The fields named "velocity" are arguably more like "acceleration,"
|
||||
// The fields named "velocity" are arguably more like "acceleration,"
|
||||
// and the fields named "fractional" are arguably more like "velocity."
|
||||
//
|
||||
// ...is what I WOULD say if the "fractional" fields weren't AND'd with
|
||||
// ...is what I WOULD say if the "fractional" fields weren't AND'd with
|
||||
// 0xFF after every frame.
|
||||
|
||||
void AnimFlyingSandCrescent(struct Sprite *sprite)
|
||||
|
|
@ -926,12 +926,7 @@ static void AnimRolloutParticle(struct Sprite *sprite)
|
|||
|
||||
static u8 GetRolloutCounter(void)
|
||||
{
|
||||
u8 retVal = gAnimDisableStructPtr->rolloutTimerStartValue - gAnimDisableStructPtr->rolloutTimer;
|
||||
u8 var0 = retVal - 1;
|
||||
if (var0 > 4)
|
||||
retVal = 1;
|
||||
|
||||
return retVal;
|
||||
return gAnimDisableStructPtr->rolloutTimer + 1;
|
||||
}
|
||||
|
||||
void AnimRockTomb(struct Sprite *sprite)
|
||||
|
|
|
|||
|
|
@ -3231,7 +3231,6 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
|
|||
gLastHitBy[battler] = 0xFF;
|
||||
|
||||
gBattleStruct->lastTakenMove[battler] = 0;
|
||||
gBattleStruct->metronomeItemCounter[battler] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
|
||||
|
|
@ -3350,7 +3349,6 @@ const u8* FaintClearSetData(u32 battler)
|
|||
gLastHitBy[battler] = 0xFF;
|
||||
|
||||
gBattleStruct->choicedMove[battler] = MOVE_NONE;
|
||||
gBattleStruct->metronomeItemCounter[battler] = 0;
|
||||
gBattleStruct->lastTakenMove[battler] = MOVE_NONE;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
|
|||
static void ResetValuesForCalledMove(void);
|
||||
static bool32 TrySymbiosis(u32 battler, u32 itemId, bool32 moveEnd);
|
||||
static bool32 CanAbilityShieldActivateForBattler(u32 battler);
|
||||
static void SetSameMoveTurnValues(u32 moveEffect);
|
||||
static void TryClearChargeVolatile(u32 moveType);
|
||||
static bool32 IsAnyTargetAffected(void);
|
||||
|
||||
|
|
@ -519,9 +520,9 @@ static void Cmd_cursetarget(void);
|
|||
static void Cmd_trysetspikes(void);
|
||||
static void Cmd_setvolatile(void);
|
||||
static void Cmd_trysetperishsong(void);
|
||||
static void Cmd_handlerollout(void);
|
||||
static void Cmd_unused_0xb3(void);
|
||||
static void Cmd_jumpifconfusedandstatmaxed(void);
|
||||
static void Cmd_handlefurycutter(void);
|
||||
static void Cmd_unused_0xb5(void);
|
||||
static void Cmd_setembargo(void);
|
||||
static void Cmd_presentdamagecalculation(void);
|
||||
static void Cmd_setsafeguard(void);
|
||||
|
|
@ -778,9 +779,9 @@ void (*const gBattleScriptingCommandsTable[])(void) =
|
|||
Cmd_trysetspikes, //0xB0
|
||||
Cmd_setvolatile, //0xB1
|
||||
Cmd_trysetperishsong, //0xB2
|
||||
Cmd_handlerollout, //0xB3
|
||||
Cmd_unused_0xb3, //0xB3
|
||||
Cmd_jumpifconfusedandstatmaxed, //0xB4
|
||||
Cmd_handlefurycutter, //0xB5
|
||||
Cmd_unused_0xb5, //0xB5
|
||||
Cmd_setembargo, //0xB6
|
||||
Cmd_presentdamagecalculation, //0xB7
|
||||
Cmd_setsafeguard, //0xB8
|
||||
|
|
@ -1022,6 +1023,48 @@ bool32 IsMoveNotAllowedInSkyBattles(u32 move)
|
|||
return (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove));
|
||||
}
|
||||
|
||||
static void SetSameMoveTurnValues(u32 moveEffect)
|
||||
{
|
||||
bool32 increment = IsAnyTargetAffected()
|
||||
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||||
&& gLastResultingMoves[gBattlerAttacker] == gCurrentMove;
|
||||
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_FURY_CUTTER:
|
||||
if (increment && gDisableStructs[gBattlerAttacker].furyCutterCounter < 5)
|
||||
gDisableStructs[gBattlerAttacker].furyCutterCounter++;
|
||||
else
|
||||
gDisableStructs[gBattlerAttacker].furyCutterCounter = 0;
|
||||
break;
|
||||
case EFFECT_ROLLOUT:
|
||||
if (increment && ++gDisableStructs[gBattlerAttacker].rolloutTimer < 5)
|
||||
{
|
||||
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
|
||||
gLockedMoves[gBattlerAttacker] = gCurrentMove;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE;
|
||||
gDisableStructs[gBattlerAttacker].rolloutTimer = 0;
|
||||
}
|
||||
break;
|
||||
case EFFECT_ECHOED_VOICE:
|
||||
if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) // Increment even if targets unaffected
|
||||
gBattleStruct->incrementEchoedVoice = TRUE;
|
||||
break;
|
||||
default: // not consecutive
|
||||
gDisableStructs[gBattlerAttacker].rolloutTimer = 0;
|
||||
gDisableStructs[gBattlerAttacker].furyCutterCounter = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (increment)
|
||||
gDisableStructs[gBattlerAttacker].metronomeItemCounter++;
|
||||
else
|
||||
gDisableStructs[gBattlerAttacker].metronomeItemCounter = 0;
|
||||
}
|
||||
|
||||
static void TryClearChargeVolatile(u32 moveType)
|
||||
{
|
||||
if (B_CHARGE < GEN_9) // Prior to gen9, charge is cleared during the end turn
|
||||
|
|
@ -1035,9 +1078,6 @@ static void TryClearChargeVolatile(u32 moveType)
|
|||
|
||||
static bool32 IsAnyTargetAffected(void)
|
||||
{
|
||||
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||||
return FALSE;
|
||||
|
||||
for (u32 battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
if (battler == gBattlerAttacker)
|
||||
|
|
@ -6202,7 +6242,7 @@ static void Cmd_moveend(void)
|
|||
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
|
||||
{
|
||||
gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos;
|
||||
gBattleStruct->lastMoveTarget[gBattlerAttacker] = gBattlerTarget;
|
||||
gBattleStruct->battlerState[gBattlerAttacker].lastMoveTarget = gBattlerTarget;
|
||||
}
|
||||
enum BattleMoveEffects originalEffect = GetMoveEffect(originallyUsedMove);
|
||||
if (IsBattlerAlive(gBattlerAttacker)
|
||||
|
|
@ -6831,13 +6871,6 @@ static void Cmd_moveend(void)
|
|||
}
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_SAME_MOVE_TURNS:
|
||||
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || !IsAnyTargetAffected())
|
||||
gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0;
|
||||
else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT)
|
||||
gBattleStruct->metronomeItemCounter[gBattlerAttacker]++;
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
|
||||
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||||
|
|
@ -6854,6 +6887,7 @@ static void Cmd_moveend(void)
|
|||
&& gBattleMons[gBattlerAttacker].volatiles.lockConfusionTurns != 1) // And won't end this turn
|
||||
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it
|
||||
|
||||
SetSameMoveTurnValues(moveEffect);
|
||||
TryClearChargeVolatile(moveType);
|
||||
ValidateSavedBattlerCounts();
|
||||
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
|
||||
|
|
@ -6876,8 +6910,6 @@ static void Cmd_moveend(void)
|
|||
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
|
||||
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
|
||||
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
|
||||
if (moveEffect == EFFECT_ECHOED_VOICE && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE))
|
||||
gBattleStruct->incrementEchoedVoice = TRUE;
|
||||
// check if Stellar type boost should be used up
|
||||
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
|
||||
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
|
||||
|
|
@ -11480,31 +11512,8 @@ static void Cmd_trysetperishsong(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
static void Cmd_handlerollout(void)
|
||||
static void Cmd_unused_0xb3(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
|
||||
{
|
||||
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE);
|
||||
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) // First hit.
|
||||
{
|
||||
gDisableStructs[gBattlerAttacker].rolloutTimer = 5;
|
||||
gDisableStructs[gBattlerAttacker].rolloutTimerStartValue = 5;
|
||||
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
|
||||
gLockedMoves[gBattlerAttacker] = gCurrentMove;
|
||||
}
|
||||
if (--gDisableStructs[gBattlerAttacker].rolloutTimer == 0) // Last hit.
|
||||
{
|
||||
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE;
|
||||
}
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
}
|
||||
|
||||
static void Cmd_jumpifconfusedandstatmaxed(void)
|
||||
|
|
@ -11518,32 +11527,8 @@ static void Cmd_jumpifconfusedandstatmaxed(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
static void Cmd_handlefurycutter(void)
|
||||
static void Cmd_unused_0xb5(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
|
||||
{
|
||||
gDisableStructs[gBattlerAttacker].furyCutterCounter = 0;
|
||||
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 max;
|
||||
|
||||
if (B_UPDATED_MOVE_DATA >= GEN_6)
|
||||
max = 3;
|
||||
else if (B_UPDATED_MOVE_DATA == GEN_5)
|
||||
max = 4;
|
||||
else
|
||||
max = 5;
|
||||
|
||||
if (gDisableStructs[gBattlerAttacker].furyCutterCounter < max
|
||||
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_2ND_HIT) // Don't increment counter on second hit
|
||||
gDisableStructs[gBattlerAttacker].furyCutterCounter++;
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
}
|
||||
|
||||
static void Cmd_setembargo(void)
|
||||
|
|
@ -16856,7 +16841,7 @@ void BS_TryInstruct(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget];
|
||||
gEffectBattler = gBattleStruct->battlerState[gBattlerTarget].lastMoveTarget;
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gBattlerPartyIndexes[gBattlerTarget]);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1949,8 +1949,6 @@ void TryClearRageAndFuryCutter(void)
|
|||
{
|
||||
if (gBattleMons[i].volatiles.rage && gChosenMoveByBattler[i] != MOVE_RAGE)
|
||||
gBattleMons[i].volatiles.rage = FALSE;
|
||||
if (gDisableStructs[i].furyCutterCounter != 0 && gChosenMoveByBattler[i] != MOVE_FURY_CUTTER)
|
||||
gDisableStructs[i].furyCutterCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2567,7 +2565,7 @@ static enum MoveCanceler CancelerPPDeduction(struct BattleContext *ctx)
|
|||
|
||||
// For item Metronome, echoed voice
|
||||
if (ctx->currentMove != gLastResultingMoves[ctx->battlerAtk] || WasUnableToUseMove(ctx->battlerAtk))
|
||||
gBattleStruct->metronomeItemCounter[ctx->battlerAtk] = 0;
|
||||
gDisableStructs[ctx->battlerAtk].metronomeItemCounter = 0;
|
||||
|
||||
if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct)
|
||||
gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct;
|
||||
|
|
@ -6877,22 +6875,21 @@ const struct TypePower gNaturalGiftTable[] =
|
|||
[ITEM_TO_BERRY(ITEM_MARANGA_BERRY)] = {TYPE_DARK, 100},
|
||||
};
|
||||
|
||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer)
|
||||
static inline u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 1; i < (5 - rolloutTimer); i++)
|
||||
for (i = 0; i < gDisableStructs[battlerAtk].rolloutTimer; i++)
|
||||
basePower *= 2;
|
||||
if (gBattleMons[battlerAtk].volatiles.defenseCurl)
|
||||
basePower *= 2;
|
||||
return basePower;
|
||||
}
|
||||
|
||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter)
|
||||
static inline u32 CalcFuryCutterBasePower(u32 battlerAtk, u32 basePower)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 1; i < furyCutterCounter; i++)
|
||||
for (u32 i = 0; i < gDisableStructs[battlerAtk].furyCutterCounter; i++)
|
||||
basePower *= 2;
|
||||
return basePower;
|
||||
return min(basePower, 160); // The duration to reach 160 depends on a gen
|
||||
}
|
||||
|
||||
static inline u32 IsFieldMudSportAffected(enum Type moveType)
|
||||
|
|
@ -6974,10 +6971,10 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
|||
basePower = 10 * (MAX_FRIENDSHIP - gBattleMons[battlerAtk].friendship) / 25;
|
||||
break;
|
||||
case EFFECT_FURY_CUTTER:
|
||||
basePower = CalcFuryCutterBasePower(basePower, gDisableStructs[battlerAtk].furyCutterCounter);
|
||||
basePower = CalcFuryCutterBasePower(battlerAtk, basePower);
|
||||
break;
|
||||
case EFFECT_ROLLOUT:
|
||||
basePower = CalcRolloutBasePower(battlerAtk, basePower, gDisableStructs[battlerAtk].rolloutTimer);
|
||||
basePower = CalcRolloutBasePower(battlerAtk, basePower);
|
||||
break;
|
||||
case EFFECT_MAGNITUDE:
|
||||
basePower = gBattleStruct->magnitudeBasePower;
|
||||
|
|
@ -8278,7 +8275,7 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff
|
|||
{
|
||||
case HOLD_EFFECT_METRONOME:
|
||||
metronomeBoostBase = PercentToUQ4_12(GetBattlerHoldEffectParam(battlerAtk));
|
||||
metronomeTurns = min(gBattleStruct->metronomeItemCounter[battlerAtk], 5);
|
||||
metronomeTurns = min(gDisableStructs[battlerAtk].metronomeItemCounter, 5);
|
||||
// according to bulbapedia this is the "correct" way to calculate the metronome boost
|
||||
// due to the limited domain of damage numbers it will never really matter whether this is off by one
|
||||
return uq4_12_add(UQ_4_12(1.0), metronomeBoostBase * metronomeTurns);
|
||||
|
|
@ -8472,11 +8469,6 @@ static inline s32 DoMoveDamageCalc(struct DamageContext *ctx)
|
|||
if (dmg != INT32_MAX)
|
||||
return dmg;
|
||||
|
||||
ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk);
|
||||
ctx->abilityDef = GetBattlerAbility(ctx->battlerDef);
|
||||
ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef);
|
||||
ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk);
|
||||
|
||||
return DoMoveDamageCalcVars(ctx);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
|
||||
[EFFECT_ROLLOUT] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectRollout,
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
.battleTvScore = 3,
|
||||
},
|
||||
|
||||
|
|
@ -636,7 +636,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
|
||||
[EFFECT_FURY_CUTTER] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectFuryCutter,
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
.battleTvScore = 2,
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,51 @@ SINGLE_BATTLE_TEST("Fury Cutter's power doubles with each use, up to 160 power")
|
|||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Fury Cutter's base power resets if the chain has been broken")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); }
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Fury Cutter's base power resets if the it is used again but a different user switched in")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Fury Cutter's power is reset if the user misses")
|
||||
TO_DO_BATTLE_TEST("Fury Cutter's power is reset if the user is switched out")
|
||||
TO_DO_BATTLE_TEST("Fury Cutter's power is reset if the trainer uses an item")
|
||||
|
|
@ -64,3 +109,23 @@ SINGLE_BATTLE_TEST("Fury Cutter counter is the same for both hits of Parental Bo
|
|||
EXPECT_NE(damage[0], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Fury Cutter's base power resets if original user is forced to switch out")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FURY_CUTTER); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FURY_CUTTER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
TURN { MOVE(player, MOVE_FURY_CUTTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,71 @@
|
|||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Write Rollout (Move Effect) test titles")
|
||||
|
||||
SINGLE_BATTLE_TEST("Rollout's power doubles after each hit and resest after the 5th hit")
|
||||
{
|
||||
s16 damage[6];
|
||||
int turn;
|
||||
int maxTurns = 6;
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_STEELIX);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROLLOUT); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { MOVE(player, MOVE_ROLLOUT); }
|
||||
} SCENE {
|
||||
for (turn = 0; turn < maxTurns; turn++) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLLOUT, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[turn]);
|
||||
}
|
||||
} THEN {
|
||||
for (turn = 1; turn < maxTurns - 1; turn++)
|
||||
EXPECT_MUL_EQ(damage[turn - 1], UQ_4_12(2.0), damage[turn]);
|
||||
EXPECT_EQ(damage[0], damage[maxTurns - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rollout's base power resets if the chain has been broken")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_ROLLOUT, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROLLOUT); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); SKIP_TURN(player); }
|
||||
TURN { MOVE(player, MOVE_ROLLOUT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLLOUT, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLLOUT, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLLOUT, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rollout resets if original user is forced to switch out")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_ROLLOUT, MOVE_CELEBRATE); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_ROLLOUT, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROLLOUT); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLLOUT, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user