diff --git a/include/battle.h b/include/battle.h index 0d54f2a614..4db357fca6 100644 --- a/include/battle.h +++ b/include/battle.h @@ -221,7 +221,7 @@ struct SpecialStatus u8 faintedHasReplacement:1; u8 focusBanded:1; u8 focusSashed:1; - u8 unused:1; + u8 unused:2; // End of byte u8 sturdied:1; u8 stormDrainRedirected:1; @@ -801,6 +801,7 @@ struct BattleStruct u8 categoryOverride; // for Z-Moves and Max Moves u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side u8 fickleBeamBoosted:1; + u8 obedienceResult:3; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/include/battle_util.h b/include/battle_util.h index de05242902..3a64b84462 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -106,6 +106,15 @@ enum CANCELLER_END2, }; +enum { + OBEYS, + DISOBEYS_LOAFS, + DISOBEYS_HITS_SELF, + DISOBEYS_FALL_ASLEEP, + DISOBEYS_WHILE_ASLEEP, + DISOBEYS_RANDOM_MOVE, +}; + extern const struct TypePower gNaturalGiftTable[]; void HandleAction_ThrowBall(void); @@ -171,7 +180,7 @@ void ClearVariousBattlerFlags(u32 battler); void HandleAction_RunBattleScript(void); u32 SetRandomTarget(u32 battler); u32 GetMoveTarget(u16 move, u8 setTarget); -u8 IsMonDisobedient(void); +u8 GetAttackerObedienceForAction(); u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating); u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating); u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index efa841a631..2e281e6084 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1284,16 +1284,40 @@ static void Cmd_attackcanceler(void) if (!(gHitMarker & HITMARKER_OBEYS) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) { - switch (IsMonDisobedient()) + switch (gBattleStruct->obedienceResult) { - case 0: + case OBEYS: break; - case 2: + case DISOBEYS_LOAFS: + // Randomly select, then print a disobedient string + // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE + gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + gMoveResultFlags |= MOVE_RESULT_MISSED; + return; + case DISOBEYS_HITS_SELF: + gBattlerTarget = gBattlerAttacker; + gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE); + gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gHitMarker |= HITMARKER_OBEYS; return; - default: + case DISOBEYS_FALL_ASLEEP: + gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; gMoveResultFlags |= MOVE_RESULT_MISSED; return; + case DISOBEYS_WHILE_ASLEEP: + gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; + gMoveResultFlags |= MOVE_RESULT_MISSED; + return; + case DISOBEYS_RANDOM_MOVE: + gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; + SetAtkCancellerForCalledMove(); + gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; + gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); + gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; + gHitMarker |= HITMARKER_OBEYS; + return; } } diff --git a/src/battle_util.c b/src/battle_util.c index 2935afc1ff..b1d3eec450 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -145,6 +145,8 @@ void HandleAction_UseMove(void) gBattleScripting.savedMoveEffect = 0; gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerAttacker); + gBattleStruct->obedienceResult = GetAttackerObedienceForAction(); + // choose move if (gProtectStructs[gBattlerAttacker].noValidMoves) { @@ -3566,7 +3568,8 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) gBattleStruct->atkCancellerTracker++; break; case CANCELLER_Z_MOVES: - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE + && gBattleStruct->obedienceResult == OBEYS) { // For Z-Mirror Move, so it doesn't play the animation twice. bool32 alreadyUsed = HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE); @@ -8331,12 +8334,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget) return targetBattler; } -static bool32 IsBattlerModernFatefulEncounter(u32 battler) -{ - return TRUE; -} - -u8 IsMonDisobedient(void) +u8 GetAttackerObedienceForAction() { s32 rnd; s32 calc; @@ -8344,40 +8342,37 @@ u8 IsMonDisobedient(void) u8 levelReferenced; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) - return 0; + return OBEYS; if (BattlerHasAi(gBattlerAttacker)) - return 0; + return OBEYS; - if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal Mew or Deoxys - { - if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT) - return 0; - if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) - return 0; - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - return 0; - if (B_OBEDIENCE_MECHANICS < GEN_8 && !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) - return 0; - if (FlagGet(FLAG_BADGE08_GET)) // Rain Badge, ignore obedience altogether - return 0; + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT) + return OBEYS; + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + return OBEYS; + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + return OBEYS; + if (B_OBEDIENCE_MECHANICS < GEN_8 && !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) + return OBEYS; + if (FlagGet(FLAG_BADGE08_GET)) // Rain Badge, ignore obedience altogether + return OBEYS; - obedienceLevel = 10; + obedienceLevel = 10; - if (FlagGet(FLAG_BADGE01_GET)) // Stone Badge - obedienceLevel = 20; - if (FlagGet(FLAG_BADGE02_GET)) // Knuckle Badge - obedienceLevel = 30; - if (FlagGet(FLAG_BADGE03_GET)) // Dynamo Badge - obedienceLevel = 40; - if (FlagGet(FLAG_BADGE04_GET)) // Heat Badge - obedienceLevel = 50; - if (FlagGet(FLAG_BADGE05_GET)) // Balance Badge - obedienceLevel = 60; - if (FlagGet(FLAG_BADGE06_GET)) // Feather Badge - obedienceLevel = 70; - if (FlagGet(FLAG_BADGE07_GET)) // Mind Badge - obedienceLevel = 80; - } + if (FlagGet(FLAG_BADGE01_GET)) // Stone Badge + obedienceLevel = 20; + if (FlagGet(FLAG_BADGE02_GET)) // Knuckle Badge + obedienceLevel = 30; + if (FlagGet(FLAG_BADGE03_GET)) // Dynamo Badge + obedienceLevel = 40; + if (FlagGet(FLAG_BADGE04_GET)) // Heat Badge + obedienceLevel = 50; + if (FlagGet(FLAG_BADGE05_GET)) // Balance Badge + obedienceLevel = 60; + if (FlagGet(FLAG_BADGE06_GET)) // Feather Badge + obedienceLevel = 70; + if (FlagGet(FLAG_BADGE07_GET)) // Mind Badge + obedienceLevel = 80; if (B_OBEDIENCE_MECHANICS >= GEN_8 && !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) @@ -8386,86 +8381,58 @@ u8 IsMonDisobedient(void) levelReferenced = gBattleMons[gBattlerAttacker].level; if (levelReferenced <= obedienceLevel) - return 0; - rnd = (Random() & 255); - calc = (levelReferenced + obedienceLevel) * rnd >> 8; + return OBEYS; + + rnd = Random(); + calc = (levelReferenced + obedienceLevel) * (rnd & 255) >> 8; if (calc < obedienceLevel) - return 0; + return OBEYS; + + // Clear the Z-Move flags if the battler is disobedient as to not waste the Z-Move + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) + { + gBattleStruct->gimmick.activated[gBattlerAttacker][GIMMICK_Z_MOVE] = FALSE; + gBattleStruct->gimmick.activeGimmick[GetBattlerSide(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = GIMMICK_NONE; + } // is not obedient if (gCurrentMove == MOVE_RAGE) gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RAGE; if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gCurrentMove == MOVE_SNORE || gCurrentMove == MOVE_SLEEP_TALK)) - { - gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; - return 1; - } + return DISOBEYS_WHILE_ASLEEP; - rnd = (Random() & 255); - calc = (levelReferenced + obedienceLevel) * rnd >> 8; + calc = (levelReferenced + obedienceLevel) * ((rnd >> 8) & 255) >> 8; if (calc < obedienceLevel) { calc = CheckMoveLimitations(gBattlerAttacker, gBitTable[gCurrMovePos], MOVE_LIMITATIONS_ALL); if (calc == ALL_MOVES_MASK) // all moves cannot be used - { - // Randomly select, then print a disobedient string - // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE - gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); - gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; - return 1; - } + return DISOBEYS_LOAFS; else // use a random move - { do - { gCurrMovePos = gChosenMovePos = MOD(Random(), MAX_MON_MOVES); - } while (gBitTable[gCurrMovePos] & calc); - - gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; - SetAtkCancellerForCalledMove(); - gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; - gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); - gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; - return 2; - } + while (gBitTable[gCurrMovePos] & calc); + return DISOBEYS_RANDOM_MOVE; } else { obedienceLevel = levelReferenced - obedienceLevel; - calc = (Random() & 255); + calc = ((rnd >> 16) & 255); if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { // try putting asleep int i; for (i = 0; i < gBattlersCount; i++) - { if (gBattleMons[i].status2 & STATUS2_UPROAR) break; - } if (i == gBattlersCount) - { - gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; - return 1; - } + return DISOBEYS_FALL_ASLEEP; } calc -= obedienceLevel; if (calc < obedienceLevel) - { - gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE); - gBattlerTarget = gBattlerAttacker; - gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; - gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; - return 2; - } + return DISOBEYS_HITS_SELF; else - { - // Randomly select, then print a disobedient string - // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE - gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); - gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; - return 1; - } + return DISOBEYS_LOAFS; } }