Fix Powder (status) config and some terrain timers not using gBattleTurnCounter (#6109)

This commit is contained in:
PhallenTree 2025-01-26 12:06:11 +00:00 committed by GitHub
parent e1e3bcfa91
commit 4158f9251f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 50 additions and 42 deletions

View File

@ -70,11 +70,11 @@ If your new Trainer Slide needs to check for beforen initalized, a function is d
```diff
void SetTrainerSlideMessage(enum DifficultyLevel, u32, u32);
void TryInitalizeFirstSTABMoveTrainerSlide(u32, u32, u32);
void TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(u32);
+ void TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(u32);
void TryInitalizeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32);
void TryInitalizeTrainerSlideEnemyMonUnaffected(u32);
void TryInitializeFirstSTABMoveTrainerSlide(u32, u32, u32);
void TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(u32);
+ void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32);
void TryInitializeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32);
void TryInitializeTrainerSlideEnemyMonUnaffected(u32);
bool32 IsTrainerSlideInitialized(enum TrainerSlideType);
```
### `src/trainer_slide.c`
@ -111,7 +111,7 @@ The function that determines if a Slide should play has different function for m
InitalizeTrainerSlide(slideId);
}
+void TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
+void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
+{
+ enum TrainerSlideType slideId = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT;
+
@ -150,13 +150,13 @@ In `BattleTurnPassed`, most Trainer Slides are checked to see if they should run
{
PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker);
+ TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget);
TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget);
+ TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget);
TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget);
gBattleCommunication[MSG_DISPLAY] = 1;
```
The actual usage of `TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit` is added and is checked whenever a critical hit is scored.
The actual usage of `TryInitializeTrainerSlideEnemyLandsFirstCriticalHit` is added and is checked whenever a critical hit is scored.
### `test/battle/trainer_slides.c`
```diff

View File

@ -128,7 +128,7 @@ struct DisableStruct
u16 overwrittenAbility; // abilities overwritten during battle (keep separate from battle history in case of switching)
u8 boosterEnergyActivates:1;
u8 roostActive:1;
u8 unbrudenActive:1;
u8 unburdenActive:1;
u8 startEmergencyExit:1;
u8 neutralizingGas:1;
u8 iceFaceActivationPrevention:1; // fixes hit escape move edge case

View File

@ -12,11 +12,11 @@ struct MessageStatus
void SetTrainerSlideMessage(enum DifficultyLevel difficulty, u32 trainerId, u32 slideId);
enum TrainerSlideTargets ShouldDoTrainerSlide(u32 battler, enum TrainerSlideType slideId);
void TryInitalizeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, u32 moveType);
void TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(u32 target);
void TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(u32 target);
void TryInitalizeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target);
void TryInitalizeTrainerSlideEnemyMonUnaffected(u32 target);
void TryInitializeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, u32 moveType);
void TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(u32 target);
void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target);
void TryInitializeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target);
void TryInitializeTrainerSlideEnemyMonUnaffected(u32 target);
bool32 IsTrainerSlideInitialized(enum TrainerSlideType slideId);
bool32 IsTrainerSlidePlayed(enum TrainerSlideType slideId);
void InitalizeTrainerSlide(enum TrainerSlideType slideId);

View File

@ -3777,6 +3777,7 @@ static void TryDoEventsBeforeFirstTurn(void)
#endif // TESTING
gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);
gBattleTurnCounter = 0;
for (i = 0; i < gBattlersCount; i++)
gBattlerByTurnOrder[i] = i;
@ -3901,7 +3902,6 @@ static void TryDoEventsBeforeFirstTurn(void)
gBattleScripting.moveendState = 0;
gBattleStruct->faintedActionsState = 0;
gBattleStruct->turnCountersTracker = 0;
gBattleTurnCounter = 0;
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
SetShellSideArmCategory();
@ -4778,7 +4778,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
// various effects
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
speed *= 2;
if (gDisableStructs[battler].unbrudenActive)
if (gDisableStructs[battler].unburdenActive)
speed *= 2;
// paralysis drop

View File

@ -2698,8 +2698,8 @@ static void Cmd_critmessage(void)
{
PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker);
TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget);
TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget);
TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget);
TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget);
gBattleCommunication[MSG_DISPLAY] = 1;
}
@ -2846,7 +2846,7 @@ static void Cmd_resultmessage(void)
stringId = STRINGID_SUPEREFFECTIVE;
}
if (stringId == STRINGID_SUPEREFFECTIVE || stringId == STRINGID_SUPEREFFECTIVETWOFOES)
TryInitalizeTrainerSlidePlayerLandsFirstSuperEffectiveHit(gBattlerTarget);
TryInitializeTrainerSlidePlayerLandsFirstSuperEffectiveHit(gBattlerTarget);
break;
case MOVE_RESULT_NOT_VERY_EFFECTIVE:
if (IsDoubleSpreadMove())
@ -3051,7 +3051,7 @@ static void CheckSetUnburden(u8 battler)
{
if (GetBattlerAbility(battler) == ABILITY_UNBURDEN)
{
gDisableStructs[battler].unbrudenActive = TRUE;
gDisableStructs[battler].unburdenActive = TRUE;
RecordAbilityBattle(battler, ABILITY_UNBURDEN);
}
}
@ -3074,7 +3074,7 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem)
RecordItemEffectBattle(battlerStealer, ItemId_GetHoldEffect(gLastUsedItem));
gBattleMons[battlerStealer].item = gLastUsedItem;
gDisableStructs[battlerStealer].unbrudenActive = FALSE;
gDisableStructs[battlerStealer].unburdenActive = FALSE;
BtlController_EmitSetMonData(battlerStealer, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); // set attacker item
MarkBattlerForControllerExec(battlerStealer);
}
@ -8606,7 +8606,7 @@ static void BestowItem(u32 battlerAtk, u32 battlerDef)
gBattleMons[battlerDef].item = gLastUsedItem;
BtlController_EmitSetMonData(battlerDef, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battlerDef].item), &gBattleMons[battlerDef].item);
MarkBattlerForControllerExec(battlerDef);
gDisableStructs[battlerDef].unbrudenActive = FALSE;
gDisableStructs[battlerDef].unburdenActive = FALSE;
}
// Called by Cmd_removeitem. itemId represents the item that was removed, not being given.
@ -14658,8 +14658,8 @@ static void Cmd_tryswapitems(void)
}
else if (oldItemAtk == ITEM_NONE && *newItemAtk != ITEM_NONE)
{
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNBURDEN && gDisableStructs[gBattlerAttacker].unbrudenActive)
gDisableStructs[gBattlerAttacker].unbrudenActive = FALSE;
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNBURDEN && gDisableStructs[gBattlerAttacker].unburdenActive)
gDisableStructs[gBattlerAttacker].unburdenActive = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_TAKEN; // nothing -> <- target's item
}
@ -16958,7 +16958,7 @@ void BS_SetRemoveTerrain(void)
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses |= statusFlag;
gFieldTimers.terrainTimer = (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}

View File

@ -1134,7 +1134,7 @@ void PrepareStringBattle(u16 stringId, u32 battler)
}
if ((stringId == STRINGID_ITDOESNTAFFECT || stringId == STRINGID_PKMNWASNTAFFECTED || stringId == STRINGID_PKMNUNAFFECTED))
TryInitalizeTrainerSlideEnemyMonUnaffected(gBattlerTarget);
TryInitializeTrainerSlideEnemyMonUnaffected(gBattlerTarget);
BtlController_EmitPrintString(battler, BUFFER_A, stringId);
MarkBattlerForControllerExec(battler);
@ -3135,6 +3135,18 @@ static inline bool32 TryFormChangeBeforeMove(void)
return TRUE;
}
static inline bool32 TryActivatePowderStatus(u32 move)
{
u32 partnerMove = gBattleMons[BATTLE_PARTNER(gBattlerAttacker)].moves[gBattleStruct->chosenMovePositions[BATTLE_PARTNER(gBattlerAttacker)]];
if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove)
return TRUE;
if (move == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE)
return TRUE;
if (move == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove)
return TRUE;
return FALSE;
}
void SetAtkCancellerForCalledMove(void)
{
gBattleStruct->atkCancellerTracker = CANCELLER_HEAL_BLOCKED;
@ -3550,7 +3562,7 @@ static void CancellerWeatherPrimal(u32 *effect)
if (HasWeatherEffect() && GetMovePower(gCurrentMove) > 0)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL) && (B_POWDER_RAIN >= GEN_7 || !TryActivatePowderStatus(gCurrentMove)))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN;
*effect = 1;
@ -3606,14 +3618,10 @@ static void CancellerPowderStatus(u32 *effect)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER)
{
u32 partnerMove = gBattleMons[BATTLE_PARTNER(gBattlerAttacker)].moves[gBattleStruct->chosenMovePositions[BATTLE_PARTNER(gBattlerAttacker)]];
if ((GetBattleMoveType(gCurrentMove) == TYPE_FIRE && !gBattleStruct->pledgeMove)
|| (gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE)
|| (gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove))
if (TryActivatePowderStatus(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE;
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD
&& (B_POWDER_RAIN < GEN_7 || !IsBattlerWeatherAffected(gBattlerAttacker, B_WEATHER_RAIN_PRIMAL)))
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE
@ -4058,9 +4066,9 @@ static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u16 *timer)
gDisableStructs[battler].terrainAbilityDone = FALSE;
if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER)
*timer = 8;
*timer = gBattleTurnCounter + 8;
else
*timer = 5;
*timer = gBattleTurnCounter + 5;
gBattleScripting.battler = battler;
return TRUE;
@ -10736,7 +10744,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
}
if (recordAbilities)
TryInitalizeFirstSTABMoveTrainerSlide(battlerDef, battlerAtk, moveType);
TryInitializeFirstSTABMoveTrainerSlide(battlerDef, battlerAtk, moveType);
return modifier;
}

View File

@ -300,7 +300,7 @@ static bool32 IsSlideInitalizedOrPlayed(enum TrainerSlideType slideId)
return FALSE;
}
void TryInitalizeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, u32 moveType)
void TryInitializeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, u32 moveType)
{
enum TrainerSlideType slideId = TRAINER_SLIDE_PLAYER_LANDS_FIRST_STAB_MOVE;
@ -316,7 +316,7 @@ void TryInitalizeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, u32 m
InitalizeTrainerSlide(slideId);
}
void TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(u32 target)
void TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(u32 target)
{
enum TrainerSlideType slideId = TRAINER_SLIDE_PLAYER_LANDS_FIRST_CRITICAL_HIT;
@ -329,7 +329,7 @@ void TryInitalizeTrainerSlidePlayerLandsFirstCriticalHit(u32 target)
InitalizeTrainerSlide(slideId);
}
void TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
{
enum TrainerSlideType slideId = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT;
@ -342,7 +342,7 @@ void TryInitalizeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
InitalizeTrainerSlide(slideId);
}
void TryInitalizeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target)
void TryInitializeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target)
{
enum TrainerSlideType slideId = TRAINER_SLIDE_PLAYER_LANDS_FIRST_SUPER_EFFECTIVE_HIT;
@ -355,7 +355,7 @@ void TryInitalizeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target)
InitalizeTrainerSlide(slideId);
}
void TryInitalizeTrainerSlideEnemyMonUnaffected(u32 target)
void TryInitializeTrainerSlideEnemyMonUnaffected(u32 target)
{
enum TrainerSlideType slideId = TRAINER_SLIDE_ENEMY_MON_UNAFFECTED;