Better implementation of the obedience fix (#5245)

Co-authored-by: Hedara <hedara90@gmail.com>
This commit is contained in:
hedara90 2024-08-25 11:20:26 +02:00 committed by GitHub
parent 36a5c6ed4f
commit 51a127fcb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 93 additions and 92 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;
}
}