diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 6500a2488..bcc85341d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -117,7 +117,7 @@ gBattleScriptsForMoveEffects:: @ 82D86A8 .4byte BattleScript_EffectSpite .4byte BattleScript_EffectFalseSwipe .4byte BattleScript_EffectHealBell - .4byte BattleScript_EffectPlaceholder103 + .4byte BattleScript_EffectAlwaysCrit .4byte BattleScript_EffectTripleKick .4byte BattleScript_EffectThief .4byte BattleScript_EffectMeanLook @@ -488,7 +488,7 @@ BattleScript_EffectEvasionDownHit: BattleScript_EffectVitalThrow: BattleScript_EffectUnused60: BattleScript_EffectFalseSwipe: -BattleScript_EffectPlaceholder103: +BattleScript_EffectAlwaysCrit: BattleScript_EffectUnused6e: BattleScript_EffectPursuit: BattleScript_EffectUnused8d: diff --git a/include/battle.h b/include/battle.h index 4dd462930..ff19aebbb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -178,6 +178,7 @@ struct DisableStruct u8 magnetRiseTimer; u8 telekinesisTimer; u8 healBlockTimer; + u8 laserFocusTimer; }; struct ProtectStruct diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 83a0a3057..0f56ca213 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -4,6 +4,7 @@ #define WINDOW_CLEAR 0x1 #define WINDOW_x80 0x80 +s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility); u8 GetBattlerTurnOrderNum(u8 battlerId); void SetMoveEffect(bool8 primary, u8 certain); void BattleDestroyYesNoCursorAt(u8 cursorPosition); diff --git a/include/constants/battle.h b/include/constants/battle.h index 74544ddc4..8f31578b7 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -156,6 +156,7 @@ #define STATUS3_MAGNET_RISE 0x4000000 #define STATUS3_HEAL_BLOCK 0x8000000 #define STATUS3_AQUA_RING 0x10000000 +#define STATUS3_LASER_FOCUS 0x20000000 #define STATUS3_SEMI_INVULNERABLE (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER) // Not really sure what a "hitmarker" is. diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 200ad6ff9..e572cf730 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -104,7 +104,7 @@ #define EFFECT_SPITE 100 #define EFFECT_FALSE_SWIPE 101 #define EFFECT_HEAL_BELL 102 -#define EFFECT_PLACEHOLDER_103 103 +#define EFFECT_ALWAYS_CRIT 103 #define EFFECT_TRIPLE_KICK 104 #define EFFECT_THIEF 105 #define EFFECT_MEAN_LOOK 106 diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index f8bfc9ec5..9e2e3a85b 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -727,6 +727,40 @@ static void RestoreBattlerData(u8 battlerId) } } +static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef) +{ + bool32 isCrit; + + switch (CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE)) + { + case -1: + case 0: + default: + isCrit = FALSE; + break; + case 1: + if (gBattleMoves[move].flags & FLAG_HIGH_CRIT && (Random() % 5 == 0)) + isCrit = TRUE; + else + isCrit = FALSE; + break; + case 2: + if (gBattleMoves[move].flags & FLAG_HIGH_CRIT && (Random() % 2 == 0)) + isCrit = TRUE; + else if (!(gBattleMoves[move].flags & FLAG_HIGH_CRIT) && (Random() % 4) == 0) + isCrit = TRUE; + else + isCrit = FALSE; + break; + case 3: + case 4: + isCrit = TRUE; + break; + } + + return isCrit; +} + s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) { s32 dmg; @@ -737,7 +771,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) SetBattlerData(battlerAtk); SetBattlerData(battlerDef); - dmg = CalculateMoveDamage(move, battlerAtk, battlerDef, gBattleMoves[move].type, 0, FALSE, FALSE); + dmg = CalculateMoveDamage(move, battlerAtk, battlerDef, gBattleMoves[move].type, 0, AI_GetIfCrit(move, battlerAtk, battlerDef), FALSE); RestoreBattlerData(battlerAtk); RestoreBattlerData(battlerDef); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index effce629f..4c5f43b88 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -627,9 +627,6 @@ static const struct StatFractions sAccuracyStageRatios[] = { 3, 1}, // +6 }; -// The chance is 1/N for each stage. -static const u16 sCriticalHitChance[] = {16, 8, 4, 3, 2}; - static const u32 sStatusFlagsForMoveEffects[] = { 0x00000000, @@ -1298,33 +1295,62 @@ static void atk03_ppreduce(void) gBattlescriptCurrInstr++; } -static void atk04_critcalc(void) +s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility) { - u8 holdEffect; - u16 item, critChance; + s32 critChance = 0; + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); + u32 abilityDef = GetBattlerAbility(gBattlerTarget); - item = gBattleMons[gBattlerAttacker].item; - - if (item == ITEM_ENIGMA_BERRY) - holdEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; + if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT + || gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) + { + critChance = -1; + } + else if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR) + { + if (recordAbility) + RecordAbilityBattle(battlerDef, abilityDef); + critChance = -1; + } + else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS + || gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT + || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)) + { + critChance = 4; + } else - holdEffect = ItemId_GetHoldEffect(item); + { + u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); - gPotentialItemEffectBattler = gBattlerAttacker; - - critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) - + ((gBattleMoves[gCurrentMove].flags & FLAG_HIGH_CRIT) != 0) - + (holdEffect == HOLD_EFFECT_SCOPE_LENS) - + 2 * (holdEffect == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY) - + 2 * (holdEffect == HOLD_EFFECT_STICK && gBattleMons[gBattlerAttacker].species == SPECIES_FARFETCHD); + critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) + + ((gBattleMoves[gCurrentMove].flags & FLAG_HIGH_CRIT) != 0) + + (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS) + + 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY) + + 2 * (holdEffectAtk == HOLD_EFFECT_STICK && gBattleMons[gBattlerAttacker].species == SPECIES_FARFETCHD) + + (abilityAtk == ABILITY_SUPER_LUCK); + } if (critChance > 4) critChance = 4; - if ((gBattleMons[gBattlerTarget].ability != ABILITY_BATTLE_ARMOR && gBattleMons[gBattlerTarget].ability != ABILITY_SHELL_ARMOR) - && !(gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) - && !(gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) - && !(Random() % sCriticalHitChance[critChance])) + return critChance; +} + +// The chance is 1/N for each stage. +static const u8 sCriticalHitChanceGen3[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5 +static const u8 sCriticalHitChanceGen6[] = {16, 8, 2, 1, 1}; +static const u8 sCriticalHitChanceGen7[] = {24, 8, 2, 1, 1}; + +static void atk04_critcalc(void) +{ + s32 critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE); + gPotentialItemEffectBattler = gBattlerAttacker; + + if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) + gIsCriticalHit = FALSE; + else if (critChance == -1) + gIsCriticalHit = FALSE; + else if (Random() % sCriticalHitChanceGen3[critChance] == 0) gIsCriticalHit = TRUE; else gIsCriticalHit = FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index 0c7dfcf9e..2ad7508d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1209,6 +1209,7 @@ enum ENDTURN_EMBARGO, ENDTURN_LOCK_ON, ENDTURN_CHARGE, + ENDTURN_LASER_FOCUS, ENDTURN_TAUNT, ENDTURN_YAWN, ENDTURN_ITEMS2, @@ -1581,6 +1582,16 @@ u8 DoBattlerEndTurnEffects(void) } gBattleStruct->turnEffectsTracker++; break; + case ENDTURN_LASER_FOCUS: + if (gStatuses3[gActiveBattler] & STATUS3_LASER_FOCUS) + { + if (gDisableStructs[gActiveBattler].laserFocusTimer != 0) + gDisableStructs[gActiveBattler].laserFocusTimer--; + if (gDisableStructs[gActiveBattler].laserFocusTimer == 0) + gStatuses3[gActiveBattler] &= ~(STATUS3_LASER_FOCUS); + } + gBattleStruct->turnEffectsTracker++; + break; case ENDTURN_EMBARGO: if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO) {