diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 7561ba169..df8bb78d3 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -29,11 +29,11 @@ .byte 0x6 .endm - .macro adjustnormaldamage + .macro adjustdamage .byte 0x7 .endm - .macro adjustnormaldamage2 + .macro nop_08 .byte 0x8 .endm @@ -558,7 +558,7 @@ .byte 0x68 .endm - .macro adjustsetdamage + .macro nop_69 .byte 0x69 .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 21ad5a5a5..cd6244d36 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -271,8 +271,7 @@ BattleScript_HitFromAtkString:: BattleScript_HitFromCritCalc:: critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage BattleScript_HitFromAtkAnimation:: attackanimation waitanimation @@ -351,8 +350,7 @@ BattleScript_EffectAbsorb:: ppreduce critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -413,8 +411,7 @@ BattleScript_82D8B96:: movevaluescleanup critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage accuracycheck BattleScript_82D8BCF, ACC_CURR_MOVE effectivenesssound hitanimation BS_TARGET @@ -463,8 +460,7 @@ BattleScript_82D8C18:: ppreduce critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -645,9 +641,8 @@ BattleScript_DoMultiHit:: copybyte cEFFECT_CHOOSER, sMULTIHIT_EFFECT critcalc damagecalc - typecalc jumpifmovehadnoeffect BattleScript_MultiHitNoMoreHits - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -853,7 +848,7 @@ BattleScript_EffectDragonRage:: typecalc bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE setword gBattleMoveDamage, 40 - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectTrap:: @@ -889,7 +884,7 @@ BattleScript_MoveMissedDoDamage:: waitmessage 0x40 damagecalc typecalc - adjustnormaldamage + adjustdamage manipulatedamage ATK80_DMG_HALF_BY_TWO_NOT_MORE_THAN_HALF_MAX_HP bicbyte gMoveResultFlags, MOVE_RESULT_MISSED orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE @@ -910,7 +905,7 @@ BattleScript_EffectMist:: waitmessage 0x40 goto BattleScript_MoveEnd -BattleScript_EffectFocusEnergy:: +BattleScript_EffectFocusEnergy: attackcanceler attackstring ppreduce @@ -922,13 +917,13 @@ BattleScript_EffectFocusEnergy:: waitmessage 0x40 goto BattleScript_MoveEnd -BattleScript_EffectRecoil:: +BattleScript_EffectRecoil: setmoveeffect MOVE_EFFECT_RECOIL_25 | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN jumpifnotmove MOVE_STRUGGLE, BattleScript_EffectHit incrementgamestat 0x1B goto BattleScript_EffectHit -BattleScript_EffectConfuse:: +BattleScript_EffectConfuse: attackcanceler attackstring ppreduce @@ -1228,7 +1223,7 @@ BattleScript_EffectLevelDamage:: typecalc bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE dmgtolevel - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectPsywave:: @@ -1239,7 +1234,7 @@ BattleScript_EffectPsywave:: typecalc bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE psywavedamageeffect - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectCounter:: @@ -1249,7 +1244,7 @@ BattleScript_EffectCounter:: attackstring ppreduce typecalc2 - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectEncore:: @@ -1425,8 +1420,7 @@ BattleScript_DoTripleKickAttack:: copyhword gDynamicBasePower, sTRIPLE_KICK_POWER critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage jumpifmovehadnoeffect BattleScript_TripleKickNoMoreHits attackanimation waitanimation @@ -1663,9 +1657,8 @@ BattleScript_FuryCutterHit:: furycuttercalc critcalc damagecalc - typecalc jumpifmovehadnoeffect BattleScript_FuryCutterHit - adjustnormaldamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectAttract:: @@ -1751,7 +1744,7 @@ BattleScript_EffectSonicboom:: typecalc bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE setword gBattleMoveDamage, 20 - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectMorningSun:: @@ -1831,7 +1824,7 @@ BattleScript_EffectMirrorCoat:: attackstring ppreduce typecalc2 - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectSkullBash:: @@ -1873,8 +1866,7 @@ BattleScript_DoHitAllWithUndergroundBonus:: accuracycheck BattleScript_HitAllWithUndergroundBonusMissed, ACC_CURR_MOVE critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -1969,10 +1961,10 @@ BattleScript_BeatUpLoop:: trydobeatup BattleScript_BeatUpEnd, BattleScript_ButItFailed printstring STRINGID_PKMNATTACK critcalc - jumpifbyte CMP_NOT_EQUAL, gCritMultiplier, 0x2, BattleScript_BeatUpAttack + jumpifbyte CMP_NOT_EQUAL, gIsCriticalHit, TRUE, BattleScript_BeatUpAttack manipulatedamage ATK80_DMG_DOUBLED BattleScript_BeatUpAttack:: - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -2120,7 +2112,7 @@ BattleScript_EffectSpitUp:: accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE stockpiletobasedamage BattleScript_SpitUpFail typecalc - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_SpitUpFail:: pause 0x20 @@ -2425,8 +2417,7 @@ BattleScript_EffectBrickBreak:: removelightscreenreflect critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage jumpifbyte CMP_EQUAL, sB_ANIM_TURN, 0x0, BattleScript_BrickBreakAnim bicbyte gMoveResultFlags, MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE BattleScript_BrickBreakAnim:: @@ -2489,7 +2480,7 @@ BattleScript_EffectEndeavor:: jumpifmovehadnoeffect BattleScript_HitFromAtkAnimation bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE copyword gBattleMoveDamage, gHpDealt - adjustsetdamage + adjustdamage goto BattleScript_HitFromAtkAnimation BattleScript_EffectEruption:: @@ -3117,8 +3108,7 @@ BattleScript_PursuitDmgOnSwitchOut:: ppreduce critcalc damagecalc - typecalc - adjustnormaldamage + adjustdamage attackanimation waitanimation effectivenesssound @@ -3291,7 +3281,7 @@ BattleScript_BideAttack:: typecalc bicbyte gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE copyword gBattleMoveDamage, sBIDE_DMG - adjustsetdamage + adjustdamage setbyte sB_ANIM_TURN, 0x1 attackanimation waitanimation @@ -3510,7 +3500,7 @@ BattleScript_MonTookFutureAttack:: BattleScript_CheckDoomDesireMiss:: accuracycheck BattleScript_FutureAttackMiss, MOVE_DOOM_DESIRE BattleScript_FutureAttackAnimate:: - adjustnormaldamage2 + adjustdamage jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, 0x0, BattleScript_FutureHitAnimDoomDesire playanimation BS_ATTACKER, B_ANIM_FUTURE_SIGHT_HIT, NULL goto BattleScript_DoFutureAttackHit @@ -3798,7 +3788,7 @@ BattleScript_MoveUsedIsConfused:: jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x0, BattleScript_MoveUsedIsConfusedRet BattleScript_DoSelfConfusionDmg:: cancelmultiturnmoves BS_ATTACKER - adjustnormaldamage2 + adjustdamage printstring STRINGID_ITHURTCONFUSION waitmessage 0x40 effectivenesssound diff --git a/include/battle.h b/include/battle.h index 43662b7cc..74b2e5095 100644 --- a/include/battle.h +++ b/include/battle.h @@ -215,15 +215,15 @@ struct ProtectStruct struct SpecialStatus { - u8 statLowered:1; // 0x1 - u8 lightningRodRedirected:1; // 0x2 - u8 restoredBankSprite: 1; // 0x4 - u8 intimidatedPoke:1; // 0x8 - u8 traced:1; // 0x10 + u8 statLowered:1; + u8 lightningRodRedirected:1; + u8 restoredBankSprite: 1; + u8 intimidatedPoke:1; + u8 traced:1; u8 flag20:1; u8 flag40:1; u8 focusBanded:1; - u8 field1[3]; + u8 focusSashed:1; s32 dmg; s32 physicalDmg; s32 specialDmg; @@ -563,6 +563,8 @@ struct BattleStruct u8 field_2A1; u8 field_2A2; u8 debugBattler; + u8 magnitudeBasePower; + u8 presentBasePower; }; #define GET_MOVE_TYPE(move, typeArg) \ @@ -576,6 +578,7 @@ struct BattleStruct #define IS_MOVE_PHYSICAL(moveType)(moveType < TYPE_MYSTERY) #define IS_MOVE_SPECIAL(moveType)(moveType > TYPE_MYSTERY) +#define BATTLER_MAX_HP(battlerId)(gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP) #define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0)) #define IS_BATTLER_OF_TYPE(battlerId, type)((gBattleMons[battlerId].type1 == type || gBattleMons[battlerId].type2 == type)) @@ -759,7 +762,7 @@ extern u8 gBattlerFainted; extern u8 gEffectBattler; extern u8 gPotentialItemEffectBattler; extern u8 gAbsentBattlerFlags; -extern u8 gCritMultiplier; +extern u8 gIsCriticalHit; extern u8 gMultiHitCounter; extern const u8 *gBattlescriptCurrInstr; extern u32 gUnusedBattleMainVar; diff --git a/include/battle_main.h b/include/battle_main.h index 27e61c8fc..31768e844 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -65,6 +65,7 @@ void BattleTurnPassed(void); u8 IsRunningFromBattleImpossible(void); void sub_803BDA0(u8 battlerId); void SwapTurnOrder(u8 id1, u8 id2); +u32 GetBattlerTotalSpeedStat(u8 battlerId); u8 GetWhoStrikesFirst(u8 battlerId1, u8 battlerId2, bool8 ignoreChosenMoves); void RunBattleScriptCommands_PopCallbacksStack(void); void RunBattleScriptCommands(void); diff --git a/include/battle_util.h b/include/battle_util.h index 04ccced1b..fedfcba77 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -79,6 +79,7 @@ bool32 IsMoveMakingContact(u16 move, u8 battlerAtk); bool32 IsBattlerGrounded(u8 battlerId); u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move); u32 GetBattlerWeight(u8 battlerId); -s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit); +s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor); +u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 31cdbb6ce..80ed2b7e2 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -151,6 +151,8 @@ #define STATUS3_SMACKED_DOWN 0x200000 #define STATUS3_ME_FIRST 0x400000 #define STATUS3_TELEKINESIS 0x800000 +#define STATUS3_UNBURDEN 0x1000000 +#define STATUS3_MIRACLE_EYED 0x2000000 #define STATUS3_SEMI_INVULNERABLE (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER) // Not really sure what a "hitmarker" is. @@ -190,6 +192,8 @@ #define SIDE_STATUS_FUTUREATTACK (1 << 6) #define SIDE_STATUS_MIST (1 << 8) #define SIDE_STATUS_SPIKES_DAMAGED (1 << 9) +#define SIDE_STATUS_TAILWIND (1 << 10) +#define SIDE_STATUS_AURORA_VEIL (1 << 11) // Field affecting statuses. #define STATUS_FIELD_MAGIC_ROOM 0x1 diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index 60c65e039..e788e7147 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -96,9 +96,16 @@ #define HOLD_EFFECT_BLACK_SLUDGE 90 #define HOLD_EFFECT_DESTINY_KNOT 91 #define HOLD_EFFECT_SHED_SHELL 92 +#define HOLD_EFFECT_QUICK_POWDER 93 +#define HOLD_EFFECT_ADAMANT_ORB 94 +#define HOLD_EFFECT_LUSTROUS_ORB 95 +#define HOLD_EFFECT_GRISEOUS_ORB 96 // Gen5 hold effects #define HOLD_EFFECT_FLOAT_STONE 115 +#define HOLD_EFFECT_WISE_GLASSES 116 +#define HOLD_EFFECT_EVIOLITE 117 +#define HOLD_EFFECT_ASSAULT_VEST 118 // Gen6 hold effects #define HOLD_EFFECT_FAIRY_POWER 130 diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 821ad3e5b..b176964bc 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -86,6 +86,15 @@ #define FLAG_MIRROR_MOVE_AFFECTED 0x10 #define FLAG_KINGSROCK_AFFECTED 0x20 #define FLAG_HIGH_CRIT 0x40 +#define FLAG_RECKLESS_BOOST 0x80 +#define FLAG_IRON_FIST_BOOST 0x100 +#define FLAG_SHEER_FORCE_BOOST 0x200 +#define FLAG_STRONG_JAW_BOOST 0x400 +#define FLAG_MEGA_LAUNCHER_BOOST 0x800 +#define FLAG_STAT_STAGES_IGNORED 0x1000 +#define FLAG_DMG_MINIMIZE 0x2000 +#define FLAG_DMG_UNDERGROUND 0x4000 +#define FLAG_DMG_UNDERWATER 0x8000 // Split defines. #define SPLIT_PHYSICAL 0x0 diff --git a/include/constants/species.h b/include/constants/species.h index f698ada14..115ea0357 100644 --- a/include/constants/species.h +++ b/include/constants/species.h @@ -447,4 +447,10 @@ #define NUM_SPECIES SPECIES_EGG +// Todo +#define SPECIES_DIALGA 0 +#define SPECIES_PALKIA 0 +#define SPECIES_GIRATINA 0 +#define SPECIES_CHERRIM 0 + #endif // GUARD_CONSTANTS_SPECIES_H diff --git a/include/global.h b/include/global.h index 7153ef260..0d5dfb8b7 100644 --- a/include/global.h +++ b/include/global.h @@ -34,6 +34,7 @@ // Converts a number to Q4.12 fixed-point format #define Q_4_12(n) ((s16)((n) * 4096)) +#define UQ_4_12(n) ((u16)((n) * 4096)) // Converts a number to Q24.8 fixed-point format #define Q_24_8(n) ((s32)((n) * 256)) @@ -43,10 +44,15 @@ // Converts a Q4.12 fixed-point format number to a regular integer #define Q_4_12_TO_INT(n) ((int)((n) / 4096)) +#define UQ_4_12_TO_INT(n) ((int)((n) / 4096)) // Converts a Q24.8 fixed-point format number to a regular integer #define Q_24_8_TO_INT(n) ((int)((n) >> 8)) +// Rounding value for Q4.12 fixed-point format +#define Q_4_12_ROUND ((1) << (12 - 1)) +#define UQ_4_12_ROUND ((1) << (12 - 1)) + #define PARTY_SIZE 6 #define POKEMON_SLOTS_NUMBER 412 diff --git a/include/pokemon.h b/include/pokemon.h index f7166a60f..16829bdb5 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -363,7 +363,7 @@ struct BattleMove u8 secondaryEffectChance; u8 target; s8 priority; - u8 flags; + u32 flags; u8 split; }; diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index 850dbf030..f4eca8c8e 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -1180,7 +1180,6 @@ static void BattleAICmd_get_how_powerful_move_is(void) gDynamicBasePower = 0; *(&gBattleStruct->dynamicMoveType) = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; for (checkedMove = 0; checkedMove < 4; checkedMove++) { @@ -1460,7 +1459,7 @@ static void BattleAICmd_get_highest_type_effectiveness(void) dynamicMoveType = &gBattleStruct->dynamicMoveType; *dynamicMoveType = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; + gIsCriticalHit = 1; AI_THINKING_STRUCT->funcResult = 0; for (i = 0; i < 4; i++) @@ -1499,7 +1498,7 @@ static void BattleAICmd_if_type_effectiveness(void) gDynamicBasePower = 0; gBattleStruct->dynamicMoveType = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; + gIsCriticalHit = 1; gBattleMoveDamage = AI_EFFECTIVENESS_x1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; @@ -1710,7 +1709,7 @@ static void BattleAICmd_if_can_faint(void) gDynamicBasePower = 0; gBattleStruct->dynamicMoveType = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; + gIsCriticalHit = 1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; AI_CalcDmg(sBattler_AI, gBattlerTarget); TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); @@ -1738,7 +1737,7 @@ static void BattleAICmd_if_cant_faint(void) gDynamicBasePower = 0; gBattleStruct->dynamicMoveType = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; + gIsCriticalHit = 1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; AI_CalcDmg(sBattler_AI, gBattlerTarget); TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 559323ee7..f4d167275 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -736,7 +736,6 @@ u8 GetMostSuitableMonToSwitchInto(void) gDynamicBasePower = 0; gBattleStruct->dynamicMoveType = 0; gMoveResultFlags = 0; - gCritMultiplier = 1; bestDmg = 0; bestMonId = 6; diff --git a/src/battle_main.c b/src/battle_main.c index 4bc5b6eb0..2a7e016e9 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -231,7 +231,7 @@ EWRAM_DATA u8 gBattlerFainted = 0; EWRAM_DATA u8 gEffectBattler = 0; EWRAM_DATA u8 gPotentialItemEffectBattler = 0; EWRAM_DATA u8 gAbsentBattlerFlags = 0; -EWRAM_DATA u8 gCritMultiplier = 0; +EWRAM_DATA u8 gIsCriticalHit = FALSE; EWRAM_DATA u8 gMultiHitCounter = 0; EWRAM_DATA const u8 *gBattlescriptCurrInstr = NULL; EWRAM_DATA u32 gUnusedBattleMainVar = 0; @@ -4615,99 +4615,80 @@ void SwapTurnOrder(u8 id1, u8 id2) gBattlerByTurnOrder[id2] = temp; } +u32 GetBattlerTotalSpeedStat(u8 battlerId) +{ + u32 speed = gBattleMons[battlerId].speed; + u32 ability = GetBattlerAbility(battlerId); + u32 holdEffect = GetBattlerHoldEffect(battlerId, TRUE); + + // weather abilities + if (WEATHER_HAS_EFFECT) + { + if (ability == ABILITY_SWIFT_SWIM && gBattleWeather & WEATHER_RAIN_ANY) + speed *= 2; + else if (ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY) + speed *= 2; + else if (ability == ABILITY_SAND_RUSH && gBattleWeather & WEATHER_SANDSTORM_ANY) + speed *= 2; + else if (ability == ABILITY_SLUSH_RUSH && gBattleWeather & WEATHER_HAIL_ANY) + speed *= 2; + } + + // other abilities + if (ability == ABILITY_QUICK_FEET && gBattleMons[battlerId].status1 & STATUS1_ANY) + speed = (speed * 150) / 100; + else if (ability == ABILITY_SURGE_SURFER && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + speed *= 2; + + // stat stages + speed *= gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPEED]][0]; + speed /= gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPEED]][1]; + + // player's badge boost + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000 | BATTLE_TYPE_FRONTIER)) + && FlagGet(FLAG_BADGE03_GET) + && GetBattlerSide(battlerId) == B_SIDE_PLAYER) + { + speed = (speed * 110) / 100; + } + + // item effects + if (holdEffect == HOLD_EFFECT_MACHO_BRACE) + speed /= 2; + else if (holdEffect == HOLD_EFFECT_IRON_BALL) + speed /= 2; + else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF) + speed = (speed * 150) / 100; + else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battlerId].species == SPECIES_DITTO && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) + speed *= 2; + + // various effects + if (gSideStatuses[GET_BATTLER_SIDE(battlerId)] & SIDE_STATUS_TAILWIND) + speed *= 2; + if (gStatuses3[battlerId] & STATUS3_UNBURDEN) + speed *= 2; + + // paralysis drop + if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET) + speed /= 4; + + return speed; +} + u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) { u8 strikesFirst = 0; - u8 speedMultiplierBattler1 = 0, speedMultiplierBattler2 = 0; u32 speedBattler1 = 0, speedBattler2 = 0; - u8 holdEffect = 0; - u8 holdEffectParam = 0; u16 moveBattler1 = 0, moveBattler2 = 0; - if (WEATHER_HAS_EFFECT) - { - if ((gBattleMons[battler1].ability == ABILITY_SWIFT_SWIM && gBattleWeather & WEATHER_RAIN_ANY) - || (gBattleMons[battler1].ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY)) - speedMultiplierBattler1 = 2; - else - speedMultiplierBattler1 = 1; - - if ((gBattleMons[battler2].ability == ABILITY_SWIFT_SWIM && gBattleWeather & WEATHER_RAIN_ANY) - || (gBattleMons[battler2].ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY)) - speedMultiplierBattler2 = 2; - else - speedMultiplierBattler2 = 1; - } - else - { - speedMultiplierBattler1 = 1; - speedMultiplierBattler2 = 1; - } - - speedBattler1 = (gBattleMons[battler1].speed * speedMultiplierBattler1) - * (gStatStageRatios[gBattleMons[battler1].statStages[STAT_SPEED]][0]) - / (gStatStageRatios[gBattleMons[battler1].statStages[STAT_SPEED]][1]); - - if (gBattleMons[battler1].item == ITEM_ENIGMA_BERRY) - { - holdEffect = gEnigmaBerries[battler1].holdEffect; - holdEffectParam = gEnigmaBerries[battler1].holdEffectParam; - } - else - { - holdEffect = ItemId_GetHoldEffect(gBattleMons[battler1].item); - holdEffectParam = ItemId_GetHoldEffectParam(gBattleMons[battler1].item); - } - - // badge boost - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000 | BATTLE_TYPE_FRONTIER)) - && FlagGet(FLAG_BADGE03_GET) - && GetBattlerSide(battler1) == B_SIDE_PLAYER) - { - speedBattler1 = (speedBattler1 * 110) / 100; - } - - if (holdEffect == HOLD_EFFECT_MACHO_BRACE) - speedBattler1 /= 2; - - if (gBattleMons[battler1].status1 & STATUS1_PARALYSIS) - speedBattler1 /= 4; - - if (holdEffect == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * holdEffectParam) / 100) + speedBattler1 = GetBattlerTotalSpeedStat(battler1); + if (GetBattlerHoldEffect(battler1, TRUE) == HOLD_EFFECT_QUICK_CLAW + && gRandomTurnNumber < (0xFFFF * GetBattlerHoldEffectParam(battler1)) / 100) speedBattler1 = UINT_MAX; - // check second battlerId's speed - - speedBattler2 = (gBattleMons[battler2].speed * speedMultiplierBattler2) - * (gStatStageRatios[gBattleMons[battler2].statStages[STAT_SPEED]][0]) - / (gStatStageRatios[gBattleMons[battler2].statStages[STAT_SPEED]][1]); - - if (gBattleMons[battler2].item == ITEM_ENIGMA_BERRY) - { - holdEffect = gEnigmaBerries[battler2].holdEffect; - holdEffectParam = gEnigmaBerries[battler2].holdEffectParam; - } - else - { - holdEffect = ItemId_GetHoldEffect(gBattleMons[battler2].item); - holdEffectParam = ItemId_GetHoldEffectParam(gBattleMons[battler2].item); - } - - // badge boost - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000 | BATTLE_TYPE_FRONTIER)) - && FlagGet(FLAG_BADGE03_GET) - && GetBattlerSide(battler2) == B_SIDE_PLAYER) - { - speedBattler2 = (speedBattler2 * 110) / 100; - } - - if (holdEffect == HOLD_EFFECT_MACHO_BRACE) - speedBattler2 /= 2; - - if (gBattleMons[battler2].status1 & STATUS1_PARALYSIS) - speedBattler2 /= 4; - - if (holdEffect == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * holdEffectParam) / 100) + speedBattler2 = GetBattlerTotalSpeedStat(battler2); + if (GetBattlerHoldEffect(battler2, TRUE) == HOLD_EFFECT_QUICK_CLAW + && gRandomTurnNumber < (0xFFFF * GetBattlerHoldEffectParam(battler2)) / 100) speedBattler2 = UINT_MAX; if (ignoreChosenMoves) @@ -5295,7 +5276,7 @@ static void HandleAction_UseMove(void) return; } - gCritMultiplier = 1; + gIsCriticalHit = FALSE; gBattleStruct->atkCancellerTracker = 0; gMoveResultFlags = 0; gMultiHitCounter = 0; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 915fe59d6..b8acec8a5 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -97,8 +97,8 @@ static void atk03_ppreduce(void); static void atk04_critcalc(void); static void atk05_damagecalc(void); static void atk06_typecalc(void); -static void atk07_adjustnormaldamage(void); -static void atk08_adjustnormaldamage2(void); +static void atk07_adjustdamage(void); +static void atk08_nop(void); static void atk09_attackanimation(void); static void atk0A_waitanimation(void); static void atk0B_healthbarupdate(void); @@ -195,7 +195,7 @@ static void atk65_status2animation(void); static void atk66_chosenstatusanimation(void); static void atk67_yesnobox(void); static void atk68_cancelallactions(void); -static void atk69_adjustsetdamage(void); +static void atk69_nop(void); static void atk6A_removeitem(void); static void atk6B_atknameinbuff1(void); static void atk6C_drawlvlupbox(void); @@ -349,8 +349,8 @@ void (* const gBattleScriptingCommandsTable[])(void) = atk04_critcalc, atk05_damagecalc, atk06_typecalc, - atk07_adjustnormaldamage, - atk08_adjustnormaldamage2, + atk07_adjustdamage, + atk08_nop, atk09_attackanimation, atk0A_waitanimation, atk0B_healthbarupdate, @@ -447,7 +447,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = atk66_chosenstatusanimation, atk67_yesnobox, atk68_cancelallactions, - atk69_adjustsetdamage, + atk69_nop, atk6A_removeitem, atk6B_atknameinbuff1, atk6C_drawlvlupbox, @@ -1273,9 +1273,9 @@ static void atk04_critcalc(void) && !(gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) && !(gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) && !(Random() % sCriticalHitChance[critChance])) - gCritMultiplier = 2; + gIsCriticalHit = TRUE; else - gCritMultiplier = 1; + gIsCriticalHit = FALSE; gBattlescriptCurrInstr++; } @@ -1285,7 +1285,7 @@ static void atk05_damagecalc(void) u8 moveType; GET_MOVE_TYPE(gCurrentMove, moveType); - gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gCritMultiplier); + gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE); gBattlescriptCurrInstr++; } @@ -1610,120 +1610,60 @@ u8 AI_TypeCalc(u16 move, u16 targetSpecies, u8 targetAbility) return flags; } -// Multiplies the damage by a random factor between 85% to 100% inclusive -static inline void ApplyRandomDmgMultiplier(void) -{ - u16 rand = Random(); - u16 randPercent = 100 - (rand % 16); - - if (gBattleMoveDamage != 0) - { - gBattleMoveDamage *= randPercent; - gBattleMoveDamage /= 100; - if (gBattleMoveDamage == 0) - gBattleMoveDamage = 1; - } -} - -static void Unused_ApplyRandomDmgMultiplier(void) -{ - ApplyRandomDmgMultiplier(); -} - -static void atk07_adjustnormaldamage(void) +static void atk07_adjustdamage(void) { u8 holdEffect, param; - ApplyRandomDmgMultiplier(); - + holdEffect = GetBattlerHoldEffect(gBattlerTarget, TRUE); if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) - { - holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; param = gEnigmaBerries[gBattlerTarget].holdEffectParam; - } else - { - holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); - } gPotentialItemEffectBattler = gBattlerTarget; + if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) + goto END; + if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) + goto END; + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) { RecordItemEffectBattle(gBattlerTarget, holdEffect); gSpecialStatuses[gBattlerTarget].focusBanded = 1; } - if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) - goto END; - if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE && !gProtectStructs[gBattlerTarget].endured - && !gSpecialStatuses[gBattlerTarget].focusBanded) - goto END; - - if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) - goto END; - - gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; - - if (gProtectStructs[gBattlerTarget].endured) - { - gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; - } - else if (gSpecialStatuses[gBattlerTarget].focusBanded) - { - gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; - gLastUsedItem = gBattleMons[gBattlerTarget].item; - } - - END: - gBattlescriptCurrInstr++; -} - -static void atk08_adjustnormaldamage2(void) // The same as 0x7 except it doesn't check for false swipe move effect. -{ - u8 holdEffect, param; - - ApplyRandomDmgMultiplier(); - - if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) - { - holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; - param = gEnigmaBerries[gBattlerTarget].holdEffectParam; - } - else - { - holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); - param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); - } - - gPotentialItemEffectBattler = gBattlerTarget; - - if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) + else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && BATTLER_MAX_HP(gBattlerTarget)) { RecordItemEffectBattle(gBattlerTarget, holdEffect); - gSpecialStatuses[gBattlerTarget].focusBanded = 1; + gSpecialStatuses[gBattlerTarget].focusSashed = 1; } - if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) - goto END; - if (!gProtectStructs[gBattlerTarget].endured && !gSpecialStatuses[gBattlerTarget].focusBanded) - goto END; - if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) + + if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE + && !gProtectStructs[gBattlerTarget].endured + && !gSpecialStatuses[gBattlerTarget].focusBanded + && !gSpecialStatuses[gBattlerTarget].focusSashed) goto END; + // Handle reducing the dmg to 1 hp gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; if (gProtectStructs[gBattlerTarget].endured) { gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; } - else if (gSpecialStatuses[gBattlerTarget].focusBanded) + else if (gSpecialStatuses[gBattlerTarget].focusBanded || gSpecialStatuses[gBattlerTarget].focusSashed) { gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; gLastUsedItem = gBattleMons[gBattlerTarget].item; } - END: - gBattlescriptCurrInstr++; +END: + gBattlescriptCurrInstr++; +} + +static void atk08_nop(void) +{ + } static void atk09_attackanimation(void) @@ -1952,7 +1892,7 @@ static void atk0D_critmessage(void) { if (gBattleControllerExecFlags == 0) { - if (gCritMultiplier == 2 && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + if (gIsCriticalHit == TRUE && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker); gBattleCommunication[MSG_DISPLAY] = 1; @@ -3865,7 +3805,7 @@ static void atk24(void) static void MoveValuesCleanUp(void) { gMoveResultFlags = 0; - gCritMultiplier = 1; + gIsCriticalHit = FALSE; gBattleCommunication[MOVE_EFFECT_BYTE] = 0; gBattleCommunication[6] = 0; gHitMarker &= ~(HITMARKER_DESTINYBOND); @@ -6100,56 +6040,14 @@ static void atk68_cancelallactions(void) gBattlescriptCurrInstr++; } -static void atk69_adjustsetdamage(void) // The same as 0x7, except there's no random damage multiplier. +static void atk69_nop(void) { - u8 holdEffect, param; - - if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) - { - holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; - param = gEnigmaBerries[gBattlerTarget].holdEffectParam; - } - else - { - holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); - param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); - } - - gPotentialItemEffectBattler = gBattlerTarget; - - if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) - { - RecordItemEffectBattle(gBattlerTarget, holdEffect); - gSpecialStatuses[gBattlerTarget].focusBanded = 1; - } - if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) - goto END; - if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE && !gProtectStructs[gBattlerTarget].endured - && !gSpecialStatuses[gBattlerTarget].focusBanded) - goto END; - - if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) - goto END; - - gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; - - if (gProtectStructs[gBattlerTarget].endured) - { - gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; - } - else if (gSpecialStatuses[gBattlerTarget].focusBanded) - { - gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; - gLastUsedItem = gBattleMons[gBattlerTarget].item; - } - - END: - gBattlescriptCurrInstr++; + gBattlescriptCurrInstr++; } static void atk6A_removeitem(void) { - u16* usedHeldItem; + u16 *usedHeldItem; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); diff --git a/src/battle_tv.c b/src/battle_tv.c index 783ecc95d..2bad9ff4b 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -1430,14 +1430,7 @@ static void TrySetBattleSeminarShow(void) { u8 moveResultFlags; u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)]; - gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, gBattleMoves[gCurrentMove].type, powerOverride, FALSE); - - if (gStatuses3[gBattlerAttacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) - gBattleMoveDamage *= 2; - if (gProtectStructs[gBattlerAttacker].helpingHand) - gBattleMoveDamage = gBattleMoveDamage * 15 / 10; - - moveResultFlags = TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); + gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, gBattleMoves[gCurrentMove].type, powerOverride, FALSE, FALSE); dmgByMove[i] = gBattleMoveDamage; if (dmgByMove[i] == 0 && !(moveResultFlags & MOVE_RESULT_NO_EFFECT)) dmgByMove[i] = 1; diff --git a/src/battle_util.c b/src/battle_util.c index d5abd6d25..cf88d168d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle.h" +#include "battle_interface.h" #include "constants/battle_script_commands.h" #include "constants/abilities.h" #include "constants/moves.h" @@ -148,6 +149,141 @@ static const u8 sHoldEffectToType[][2] = {HOLD_EFFECT_FAIRY_POWER, TYPE_FAIRY}, }; +// percent in UQ_4_12 format +static const u16 sPercentToModifier[] = +{ + UQ_4_12(0.00), // 0 + UQ_4_12(0.01), // 1 + UQ_4_12(0.02), // 2 + UQ_4_12(0.03), // 3 + UQ_4_12(0.04), // 4 + UQ_4_12(0.05), // 5 + UQ_4_12(0.06), // 6 + UQ_4_12(0.07), // 7 + UQ_4_12(0.08), // 8 + UQ_4_12(0.09), // 9 + UQ_4_12(0.10), // 10 + UQ_4_12(0.11), // 11 + UQ_4_12(0.12), // 12 + UQ_4_12(0.13), // 13 + UQ_4_12(0.14), // 14 + UQ_4_12(0.15), // 15 + UQ_4_12(0.16), // 16 + UQ_4_12(0.17), // 17 + UQ_4_12(0.18), // 18 + UQ_4_12(0.19), // 19 + UQ_4_12(0.20), // 20 + UQ_4_12(0.21), // 21 + UQ_4_12(0.22), // 22 + UQ_4_12(0.23), // 23 + UQ_4_12(0.24), // 24 + UQ_4_12(0.25), // 25 + UQ_4_12(0.26), // 26 + UQ_4_12(0.27), // 27 + UQ_4_12(0.28), // 28 + UQ_4_12(0.29), // 29 + UQ_4_12(0.30), // 30 + UQ_4_12(0.31), // 31 + UQ_4_12(0.32), // 32 + UQ_4_12(0.33), // 33 + UQ_4_12(0.34), // 34 + UQ_4_12(0.35), // 35 + UQ_4_12(0.36), // 36 + UQ_4_12(0.37), // 37 + UQ_4_12(0.38), // 38 + UQ_4_12(0.39), // 39 + UQ_4_12(0.40), // 40 + UQ_4_12(0.41), // 41 + UQ_4_12(0.42), // 42 + UQ_4_12(0.43), // 43 + UQ_4_12(0.44), // 44 + UQ_4_12(0.45), // 45 + UQ_4_12(0.46), // 46 + UQ_4_12(0.47), // 47 + UQ_4_12(0.48), // 48 + UQ_4_12(0.49), // 49 + UQ_4_12(0.50), // 50 + UQ_4_12(0.51), // 51 + UQ_4_12(0.52), // 52 + UQ_4_12(0.53), // 53 + UQ_4_12(0.54), // 54 + UQ_4_12(0.55), // 55 + UQ_4_12(0.56), // 56 + UQ_4_12(0.57), // 57 + UQ_4_12(0.58), // 58 + UQ_4_12(0.59), // 59 + UQ_4_12(0.60), // 60 + UQ_4_12(0.61), // 61 + UQ_4_12(0.62), // 62 + UQ_4_12(0.63), // 63 + UQ_4_12(0.64), // 64 + UQ_4_12(0.65), // 65 + UQ_4_12(0.66), // 66 + UQ_4_12(0.67), // 67 + UQ_4_12(0.68), // 68 + UQ_4_12(0.69), // 69 + UQ_4_12(0.70), // 70 + UQ_4_12(0.71), // 71 + UQ_4_12(0.72), // 72 + UQ_4_12(0.73), // 73 + UQ_4_12(0.74), // 74 + UQ_4_12(0.75), // 75 + UQ_4_12(0.76), // 76 + UQ_4_12(0.77), // 77 + UQ_4_12(0.78), // 78 + UQ_4_12(0.79), // 79 + UQ_4_12(0.80), // 80 + UQ_4_12(0.81), // 81 + UQ_4_12(0.82), // 82 + UQ_4_12(0.83), // 83 + UQ_4_12(0.84), // 84 + UQ_4_12(0.85), // 85 + UQ_4_12(0.86), // 86 + UQ_4_12(0.87), // 87 + UQ_4_12(0.88), // 88 + UQ_4_12(0.89), // 89 + UQ_4_12(0.90), // 90 + UQ_4_12(0.91), // 91 + UQ_4_12(0.92), // 92 + UQ_4_12(0.93), // 93 + UQ_4_12(0.94), // 94 + UQ_4_12(0.95), // 95 + UQ_4_12(0.96), // 96 + UQ_4_12(0.97), // 97 + UQ_4_12(0.98), // 98 + UQ_4_12(0.99), // 99 + UQ_4_12(1.00), // 100 +}; + +#define X UQ_4_12 + +static const u16 sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = +{ +// normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy + {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // normal + {X(2.0), X(1.0), X(0.5), X(0.5), X(1.0), X(2.0), X(0.5), X(0.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(0.5)}, // fight + {X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // flying + {X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(0.5), X(0.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0)}, // poison + {X(1.0), X(1.0), X(0.0), X(2.0), X(1.0), X(2.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // ground + {X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0)}, // rock + {X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5)}, // bug + {X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0)}, // ghost + {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0)}, // steel + {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // mystery + {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0)}, // fire + {X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // water + {X(1.0), X(1.0), X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), X(1.0), X(0.5), X(1.0), X(0.5), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // grass + {X(1.0), X(1.0), X(2.0), X(1.0), X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // electric + {X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(0.0), X(1.0)}, // psychic + {X(1.0), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0)}, // ice + {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(0.0)}, // dragon + {X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(0.5)}, // dark + {X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0)}, // fairy + +}; + +#undef X + // code u8 GetBattlerForBattleScript(u8 caseId) { @@ -1552,7 +1688,7 @@ u8 AtkCanceller_UnableToUseMove(void) { gBattleCommunication[MULTISTRING_CHOOSER] = 1; gBattlerTarget = gBattlerAttacker; - gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE); + gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE); gProtectStructs[gBattlerAttacker].confusionSelfDmg = 1; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; } @@ -3472,7 +3608,7 @@ u8 IsMonDisobedient(void) calc -= obedienceLevel; if (calc < obedienceLevel) { - gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE); + gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE); gBattlerTarget = gBattlerAttacker; gBattlescriptCurrInstr = BattleScript_82DB6F0; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; @@ -3654,7 +3790,932 @@ u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef) } } -s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit) +static void MulModifier(u16 *modifier, u16 val) { - + *modifier = UQ_4_12_TO_INT((*modifier * val) + UQ_4_12_ROUND); +} + +static u32 ApplyModifier(u16 modifier, u32 val) +{ + return UQ_4_12_TO_INT((modifier * val) + UQ_4_12_ROUND); +} + +static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) +{ + u32 i; + u16 basePower = gBattleMoves[move].power; + u32 weight, hpFraction, speed; + + switch (gBattleMoves[move].effect) + { + case EFFECT_PLEDGE: + // todo + break; + case EFFECT_FLING: + // todo + break; + case EFFECT_ERUPTION: + basePower = gBattleMons[battlerAtk].hp * basePower / gBattleMons[battlerAtk].maxHP; + break; + case EFFECT_FLAIL: + hpFraction = GetScaledHPFraction(gBattleMons[battlerAtk].hp, gBattleMons[battlerAtk].maxHP, 48); + for (i = 0; i < sizeof(sFlailHpScaleToPowerTable); i += 2) + { + if (hpFraction <= sFlailHpScaleToPowerTable[i]) + break; + } + basePower = sFlailHpScaleToPowerTable[i + 1]; + break; + case EFFECT_RETURN: + basePower = 10 * (gBattleMons[battlerAtk].friendship) / 25; + break; + case EFFECT_FRUSTRATION: + basePower = 10 * (255 - gBattleMons[battlerAtk].friendship) / 25; + break; + case EFFECT_FURY_CUTTER: + for (i = 1; i < gDisableStructs[battlerAtk].furyCutterCounter; i++) + basePower *= 2; + break; + case EFFECT_ROLLOUT: + for (i = 1; i < (5 - gDisableStructs[battlerAtk].rolloutCounter1); i++) + basePower *= 2; + if (gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL) + basePower *= 2; + break; + case EFFECT_MAGNITUDE: + basePower = gBattleStruct->magnitudeBasePower; + break; + case EFFECT_PRESENT: + basePower = gBattleStruct->presentBasePower; + break; + case EFFECT_TRIPLE_KICK: + basePower += gBattleScripting.tripleKickPower; + break; + case EFFECT_SPIT_UP: + basePower = 100 * gDisableStructs[battlerAtk].stockpileCounter; + break; + case EFFECT_REVENGE: + if ((gProtectStructs[battlerAtk].physicalDmg + && gProtectStructs[battlerAtk].physicalBattlerId == battlerDef) + || (gProtectStructs[battlerAtk].specialDmg + && gProtectStructs[battlerAtk].specialBattlerId == battlerDef)) + basePower *= 2; + break; + case EFFECT_WEATHER_BALL: + if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_ANY) + basePower *= 2; + break; + case EFFECT_PURSUIT: + if (gCurrentActionFuncId == B_ACTION_SWITCH) + basePower *= 2; + break; + case EFFECT_NATURAL_GIFT: + // todo + break; + case EFFECT_WAKE_UP_SLAP: + if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP) + basePower *= 2; + break; + case EFFECT_SMELLINGSALT: + if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS) + basePower *= 2; + break; + case EFFECT_WRING_OUT: + basePower = 120 * gBattleMons[battlerDef].hp / gBattleMons[battlerDef].maxHP; + break; + case EFFECT_HEX: + if (gBattleMons[battlerDef].status1 & STATUS1_ANY) + basePower *= 2; + break; + case EFFECT_ASSURANCE: + if (gSpecialStatuses[battlerDef].physicalDmg != 0 || gSpecialStatuses[battlerDef].specialDmg != 0) + basePower *= 2; + break; + case EFFECT_TRUMP_CARD: + i = GetBattleMonMoveSlot(&gBattleMons[battlerAtk], move); + if (i != 4) + { + switch (gBattleMons[battlerAtk].pp[i]) + { + case 0: + basePower = 200; + break; + case 1: + basePower = 80; + break; + case 2: + basePower = 60; + break; + case 3: + basePower = 50; + break; + default: + basePower = 40; + break; + } + } + break; + case EFFECT_ACROBATICS: + if (gBattleMons[battlerAtk].item == ITEM_NONE) + basePower *= 2; + break; + case EFFECT_LOW_KICK: + weight = GetBattlerWeight(battlerDef); + for (i = 0; sWeightToDamageTable[i] != 0xFFFF; i += 2) + { + if (sWeightToDamageTable[i] > weight) + break; + } + if (sWeightToDamageTable[i] != 0xFFFF) + basePower = sWeightToDamageTable[i + 1]; + else + basePower = 120; + break; + case EFFECT_HEAT_CRASH: + weight = GetBattlerWeight(battlerAtk) / GetBattlerWeight(battlerDef); + if (weight >= 5) + basePower = 120; + else if (weight == 4) + basePower = 100; + else if (weight == 3) + basePower = 80; + else if (weight == 2) + basePower = 60; + else + basePower = 40; + break; + case EFFECT_PUNISHMENT: + basePower = 60 + (CountBattlerStatIncreases(battlerAtk, FALSE) * 20); + if (basePower > 200) + basePower = 200; + break; + case EFFECT_STORED_POWER: + basePower = 60 + (CountBattlerStatIncreases(battlerAtk, TRUE) * 20); + break; + case EFFECT_ELECTRO_BALL: + speed = GetBattlerTotalSpeedStat(battlerAtk) / GetBattlerTotalSpeedStat(battlerDef); + if (speed >= ARRAY_COUNT(sSpeedDiffToDmgTable)) + speed = ARRAY_COUNT(sSpeedDiffToDmgTable) - 1; + basePower = sSpeedDiffToDmgTable[speed]; + break; + case EFFECT_GYRO_BALL: + basePower = ((25 * GetBattlerTotalSpeedStat(battlerDef)) / GetBattlerTotalSpeedStat(battlerAtk)) + 1; + if (basePower > 150) + basePower = 150; + break; + case EFFECT_ECHOED_VOICE: + if (gFieldTimers.echoVoiceCounter != 0) + { + if (gFieldTimers.echoVoiceCounter >= 5) + basePower *= 5; + else + basePower *= gFieldTimers.echoVoiceCounter; + } + break; + case EFFECT_PAYBACK: + if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)) + basePower *= 2; + break; + case EFFECT_GUST: + case EFFECT_TWISTER: + if (gStatuses3[battlerDef] & STATUS3_ON_AIR) + basePower *= 2; + break; + case EFFECT_ROUND: + if (gChosenMoveByBattler[BATTLE_PARTNER(battlerAtk)] == MOVE_ROUND && !(gAbsentBattlerFlags & gBitTable[BATTLE_PARTNER(battlerAtk)])) + basePower *= 2; + break; + } + + if (basePower == 0) + basePower = 1; + return basePower; +} + +static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType) +{ + u32 i; + u32 holdEffectAtk, holdEffectParamAtk; + u16 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef); + u16 holdEffectModifier; + u16 modifier = UQ_4_12(1.0); + + // attacker's abilities + switch (GetBattlerAbility(battlerAtk)) + { + case ABILITY_TECHNICIAN: + if (basePower <= 60) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_FLARE_BOOST: + if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && IS_MOVE_SPECIAL(move)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_TOXIC_BOOST: + if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY && IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_RECKLESS: + if (gBattleMoves[move].flags & FLAG_RECKLESS_BOOST) + MulModifier(&modifier, UQ_4_12(1.2)); + break; + case ABILITY_IRON_FIST: + if (gBattleMoves[move].flags & FLAG_IRON_FIST_BOOST) + MulModifier(&modifier, UQ_4_12(1.2)); + break; + case ABILITY_SHEER_FORCE: + if (gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) + MulModifier(&modifier, UQ_4_12(1.3)); + break; + case ABILITY_SAND_FORCE: + if (moveType == TYPE_STEEL || moveType == TYPE_ROCK || moveType == TYPE_GROUND) + MulModifier(&modifier, UQ_4_12(1.3)); + break; + case ABILITY_RIVALRY: + if (GetGenderFromSpeciesAndPersonality(gBattleMons[battlerAtk].species, gBattleMons[battlerAtk].personality) != MON_GENDERLESS + && GetGenderFromSpeciesAndPersonality(gBattleMons[battlerDef].species, gBattleMons[battlerDef].personality) != MON_GENDERLESS) + { + if (GetGenderFromSpeciesAndPersonality(gBattleMons[battlerAtk].species, gBattleMons[battlerAtk].personality) + == GetGenderFromSpeciesAndPersonality(gBattleMons[battlerDef].species, gBattleMons[battlerDef].personality)) + MulModifier(&modifier, UQ_4_12(1.25)); + else + MulModifier(&modifier, UQ_4_12(0.75)); + } + break; + case ABILITY_ANALYTIC: + if (GetBattlerTurnOrderNum(battlerAtk) == gBattlersCount - 1 && move != MOVE_FUTURE_SIGHT && move != MOVE_DOOM_DESIRE) + MulModifier(&modifier, UQ_4_12(1.3)); + break; + case ABILITY_TOUGH_CLAWS: + if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) + MulModifier(&modifier, UQ_4_12(1.3)); + break; + case ABILITY_STRONG_JAW: + if (gBattleMoves[move].flags & FLAG_STRONG_JAW_BOOST) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_MEGA_LAUNCHER: + if (gBattleMoves[move].flags & FLAG_MEGA_LAUNCHER_BOOST) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_WATER_BUBBLE: + if (moveType == TYPE_WATER) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case ABILITY_STEELWORKER: + if (moveType == TYPE_STEEL) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + // field abilities + if ((ABILITY_ON_FIELD(ABILITY_DARK_AURA) && moveType == TYPE_DARK) + || (ABILITY_ON_FIELD(ABILITY_FAIRY_AURA) && moveType == TYPE_FAIRY)) + { + if (ABILITY_ON_FIELD(ABILITY_AURA_BREAK)) + MulModifier(&modifier, UQ_4_12(0.75)); + else + MulModifier(&modifier, UQ_4_12(1.25)); + } + + // attacker partner's abilities + if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) + { + switch (GetBattlerAbility(BATTLE_PARTNER(battlerAtk))) + { + case ABILITY_BATTERY: + if (IS_MOVE_SPECIAL(move)) + MulModifier(&modifier, UQ_4_12(1.3)); + break; + } + } + + // target's abilities + switch (GetBattlerAbility(battlerDef)) + { + case ABILITY_HEATPROOF: + case ABILITY_WATER_BUBBLE: + if (moveType == TYPE_FIRE) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + case ABILITY_DRY_SKIN: + if (moveType == TYPE_FIRE) + MulModifier(&modifier, UQ_4_12(1.25)); + break; + case ABILITY_FLUFFY: + if (IsMoveMakingContact(move, battlerAtk)) + MulModifier(&modifier, UQ_4_12(0.5)); + if (moveType == TYPE_FIRE) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + } + + holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + holdEffectParamAtk = GetBattlerHoldEffectParam(battlerAtk); + if (holdEffectParamAtk > 100) + holdEffectParamAtk = 100; + + holdEffectModifier = UQ_4_12(1.0) + sPercentToModifier[holdEffectParamAtk]; + + // attacker's hold effect + switch (holdEffectAtk) + { + case HOLD_EFFECT_MUSCLE_BAND: + if (IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_WISE_GLASSES: + if (IS_MOVE_SPECIAL(move)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_LUSTROUS_ORB: + if (gBattleMons[battlerAtk].species == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_ADAMANT_ORB: + if (gBattleMons[battlerAtk].species == SPECIES_DIALGA && (moveType == TYPE_STEEL || moveType == TYPE_DRAGON)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_GRISEOUS_ORB: + if (gBattleMons[battlerAtk].species == SPECIES_GIRATINA && (moveType == TYPE_GHOST || moveType == TYPE_DRAGON)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_SOUL_DEW: + if ((gBattleMons[battlerAtk].species == SPECIES_LATIAS || gBattleMons[battlerAtk].species == SPECIES_LATIOS) && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER)) + MulModifier(&modifier, holdEffectModifier); + break; + case HOLD_EFFECT_BUG_POWER: + case HOLD_EFFECT_STEEL_POWER: + case HOLD_EFFECT_GROUND_POWER: + case HOLD_EFFECT_ROCK_POWER: + case HOLD_EFFECT_GRASS_POWER: + case HOLD_EFFECT_DARK_POWER: + case HOLD_EFFECT_FIGHTING_POWER: + case HOLD_EFFECT_ELECTRIC_POWER: + case HOLD_EFFECT_WATER_POWER: + case HOLD_EFFECT_FLYING_POWER: + case HOLD_EFFECT_POISON_POWER: + case HOLD_EFFECT_ICE_POWER: + case HOLD_EFFECT_GHOST_POWER: + case HOLD_EFFECT_PSYCHIC_POWER: + case HOLD_EFFECT_FIRE_POWER: + case HOLD_EFFECT_DRAGON_POWER: + case HOLD_EFFECT_NORMAL_POWER: + case HOLD_EFFECT_FAIRY_POWER: + for (i = 0; i < ARRAY_COUNT(sHoldEffectToType); i++) + { + if (holdEffectAtk == sHoldEffectToType[i][0]) + { + if (moveType == sHoldEffectToType[i][1]) + MulModifier(&modifier, holdEffectModifier); + break; + } + } + break; + } + + // move effect + switch (gBattleMoves[move].effect) + { + case EFFECT_FACADE: + if (gBattleMons[battlerAtk].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_PARALYSIS)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case EFFECT_BRINE: + if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 2)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case EFFECT_VENOSHOCK: + if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case EFFECT_RETALITATE: + // todo + break; + case EFFECT_SOLARBEAM: + if (WEATHER_HAS_EFFECT && gBattleWeather & (WEATHER_HAIL_ANY | WEATHER_SANDSTORM_ANY | WEATHER_RAIN_ANY)) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + case EFFECT_BULLDOZE: + case EFFECT_MAGNITUDE: + case EFFECT_EARTHQUAKE: + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battlerDef)) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + case EFFECT_KNOCK_OFF: + if (gBattleMons[battlerDef].item != ITEM_NONE && GetBattlerAbility(battlerDef) != ABILITY_STICKY_HOLD) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + // various effecs + if (gProtectStructs[battlerAtk].helpingHand) + MulModifier(&modifier, UQ_4_12(1.5)); + if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC) + MulModifier(&modifier, UQ_4_12(2.0)); + if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST) + MulModifier(&modifier, UQ_4_12(1.5)); + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && moveType == TYPE_GRASS && IsBattlerGrounded(battlerAtk)) + MulModifier(&modifier, UQ_4_12(1.5)); + if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && moveType == TYPE_DRAGON && IsBattlerGrounded(battlerDef)) + MulModifier(&modifier, UQ_4_12(0.5)); + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && moveType == TYPE_ELECTRIC && IsBattlerGrounded(battlerAtk)) + MulModifier(&modifier, UQ_4_12(1.5)); + if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && moveType == TYPE_PSYCHIC && IsBattlerGrounded(battlerAtk)) + MulModifier(&modifier, UQ_4_12(1.5)); + + return ApplyModifier(modifier, basePower); +} + +static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit) +{ + u8 atkStage; + u32 atkStat; + u16 modifier; + + if (gBattleMoves[move].effect == EFFECT_FOUL_PLAY) + { + if (IS_MOVE_PHYSICAL(move)) + atkStat = gBattleMons[battlerDef].attack; + else + atkStat = gBattleMons[battlerDef].spAttack; + + atkStage = gBattleMons[battlerDef].statStages[STAT_ATK]; + } + else + { + if (IS_MOVE_PHYSICAL(move)) + atkStat = gBattleMons[battlerAtk].attack; + else + atkStat = gBattleMons[battlerAtk].spAttack; + + atkStage = gBattleMons[battlerAtk].statStages[STAT_ATK]; + } + + // critical hits ignore attack stat's stage drops + if (isCrit && atkStage < 6) + atkStage = 6; + // pokemon with unaware ignore attack stat changes while taking damage + if (GetBattlerAbility(battlerDef) == ABILITY_UNAWARE) + atkStage = 6; + + atkStat *= gStatStageRatios[atkStage][0]; + atkStat /= gStatStageRatios[atkStage][1]; + + // apply attack stat modifiers + modifier = UQ_4_12(1.0); + + // attacker's abilities + switch (GetBattlerAbility(battlerAtk)) + { + case ABILITY_HUGE_POWER: + case ABILITY_PURE_POWER: + if (IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case ABILITY_SLOW_START: + if (gDisableStructs[battlerAtk].slowStartTimer != 0) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + case ABILITY_SOLAR_POWER: + if (IS_MOVE_SPECIAL(move) && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_DEFEATIST: + if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerDef].maxHP / 2)) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + case ABILITY_FLASH_FIRE: + if (moveType == TYPE_FIRE && gBattleResources->flags->flags[battlerAtk] & UNKNOWN_FLAG_FLASH_FIRE) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_SWARM: + if (moveType == TYPE_BUG && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_TORRENT: + if (moveType == TYPE_WATER && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_BLAZE: + if (moveType == TYPE_FIRE && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_OVERGROW: + if (moveType == TYPE_GRASS && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_PLUS: + case ABILITY_MINUS: + if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) + { + u32 partnerAbility = GetBattlerAbility(BATTLE_PARTNER(battlerAtk)); + if (partnerAbility == ABILITY_PLUS || partnerAbility == ABILITY_MINUS) + MulModifier(&modifier, UQ_4_12(1.5)); + } + break; + case ABILITY_FLOWER_GIFT: + if (gBattleMons[battlerAtk].species == SPECIES_CHERRIM && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_HUSTLE: + if (IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + // target's abilities + switch (GetBattlerAbility(battlerDef)) + { + case ABILITY_THICK_FAT: + if (moveType == TYPE_FIRE || moveType == TYPE_ICE) + MulModifier(&modifier, UQ_4_12(0.5)); + break; + } + + // ally's abilities + if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) + { + switch (GetBattlerAbility(BATTLE_PARTNER(battlerAtk))) + { + case ABILITY_FLOWER_GIFT: + if (gBattleMons[BATTLE_PARTNER(battlerAtk)].species == SPECIES_CHERRIM) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + } + + // attacker's hold effect + switch (GetBattlerHoldEffect(battlerAtk, TRUE)) + { + case HOLD_EFFECT_THICK_CLUB: + if ((gBattleMons[battlerAtk].species == SPECIES_CUBONE || gBattleMons[battlerAtk].species == SPECIES_MAROWAK) && IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case HOLD_EFFECT_DEEP_SEA_TOOTH: + if (gBattleMons[battlerAtk].species == SPECIES_CLAMPERL && IS_MOVE_SPECIAL(move)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case HOLD_EFFECT_LIGHT_BALL: + if (gBattleMons[battlerAtk].species == SPECIES_PIKACHU) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case HOLD_EFFECT_CHOICE_BAND: + if (IS_MOVE_PHYSICAL(move)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case HOLD_EFFECT_CHOICE_SPECS: + if (IS_MOVE_SPECIAL(move)) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + return ApplyModifier(modifier, atkStat); +} + +static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit) +{ + bool32 usesDefStat; + u8 defStage; + u32 defStat, def, spDef; + u16 modifier; + + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped + { + def = gBattleMons[battlerDef].spDefense; + spDef = gBattleMons[battlerDef].defense; + } + else + { + def = gBattleMons[battlerDef].defense; + spDef = gBattleMons[battlerDef].spDefense; + } + + if (gBattleMoves[move].effect == EFFECT_PSYSHOCK || IS_MOVE_PHYSICAL(move)) // uses defense stat instead of sp.def + { + defStat = def; + defStage = gBattleMons[battlerDef].statStages[STAT_DEF]; + usesDefStat = TRUE; + } + else // is special + { + defStat = spDef; + defStage = gBattleMons[battlerDef].statStages[STAT_SPDEF]; + usesDefStat = FALSE; + } + + // critical hits ignore positive stat changes + if (isCrit && defStage > 6) + defStage = 6; + // pokemon with unaware ignore defense stat changes while dealing damage + if (GetBattlerAbility(battlerAtk) == ABILITY_UNAWARE) + defStage = 6; + // certain moves also ignore stat changes + if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED) + defStage = 6; + + defStat *= gStatStageRatios[defStage][0]; + defStat /= gStatStageRatios[defStage][1]; + + // apply defense stat modifiers + modifier = UQ_4_12(1.0); + + // target's abilities + switch (GetBattlerAbility(battlerDef)) + { + case ABILITY_MARVEL_SCALE: + if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_FUR_COAT: + if (usesDefStat) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case ABILITY_GRASS_PELT: + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_FLOWER_GIFT: + if (gBattleMons[battlerDef].species == SPECIES_CHERRIM && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && !usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + // ally's abilities + if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))) + { + switch (GetBattlerAbility(BATTLE_PARTNER(battlerDef))) + { + case ABILITY_FLOWER_GIFT: + if (gBattleMons[BATTLE_PARTNER(battlerDef)].species == SPECIES_CHERRIM && !usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + } + + // target's hold effects + switch (GetBattlerHoldEffect(battlerDef, TRUE)) + { + case HOLD_EFFECT_DEEP_SEA_SCALE: + if (gBattleMons[battlerDef].species == SPECIES_CLAMPERL && !usesDefStat) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case HOLD_EFFECT_METAL_POWDER: + if (gBattleMons[battlerDef].species == SPECIES_DITTO && usesDefStat && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) + MulModifier(&modifier, UQ_4_12(2.0)); + break; + case HOLD_EFFECT_EVIOLITE: + // todo + break; + case HOLD_EFFECT_ASSAULT_VEST: + if (!usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + } + + return ApplyModifier(modifier, defStat); +} + +static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 typeEffectivenessModifier, bool32 isCrit) +{ + u32 abilityAtk = GetBattlerAbility(battlerAtk); + u32 abilityDef = GetBattlerAbility(battlerDef); + u32 defSide = GET_BATTLER_SIDE(battlerDef); + u16 finalModifier = UQ_4_12(1.0); + + // check multiple targets in double battle + if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) + MulModifier(&finalModifier, UQ_4_12(0.75)); + + // take type effectiveness + MulModifier(&finalModifier, typeEffectivenessModifier); + + // check crit + if (isCrit) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + + // check burn + if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && gBattleMoves[move].effect != EFFECT_FACADE && abilityAtk != ABILITY_GUTS) + dmg = ApplyModifier(UQ_4_12(0.5), dmg); + + // check sunny/rain weather + if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_RAIN_ANY) + { + if (moveType == TYPE_FIRE) + dmg = ApplyModifier(UQ_4_12(0.5), dmg); + else if (moveType == TYPE_WATER) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + } + else if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) + { + if (moveType == TYPE_FIRE) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + else if (moveType == TYPE_WATER) + dmg = ApplyModifier(UQ_4_12(0.5), dmg); + } + + // check stab + if (IS_BATTLER_OF_TYPE(battlerAtk, moveType) && move != MOVE_STRUGGLE) + { + if (abilityAtk == ABILITY_ADAPTABILITY) + MulModifier(&finalModifier, UQ_4_12(2.0)); + else + MulModifier(&finalModifier, UQ_4_12(1.5)); + } + + // reflect, light screen, aurora veil + if ((gSideStatuses[defSide] & SIDE_STATUS_REFLECT && IS_MOVE_PHYSICAL(move)) + || (gSideStatuses[defSide] & SIDE_STATUS_LIGHTSCREEN && IS_MOVE_SPECIAL(move)) + || (gSideStatuses[defSide] & SIDE_STATUS_AURORA_VEIL)) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + MulModifier(&finalModifier, UQ_4_12(0.66)); + else + MulModifier(&finalModifier, UQ_4_12(0.5)); + } + + // attacker's abilities + switch (abilityAtk) + { + case ABILITY_TINTED_LENS: + if (typeEffectivenessModifier <= UQ_4_12(0.5)) + MulModifier(&finalModifier, UQ_4_12(2.0)); + break; + case ABILITY_SNIPER: + if (isCrit) + MulModifier(&finalModifier, UQ_4_12(1.5)); + break; + } + + // target's abilities + switch (abilityDef) + { + case ABILITY_MULTISCALE: + if (BATTLER_MAX_HP(battlerDef)) + MulModifier(&finalModifier, UQ_4_12(0.5)); + break; + case ABILITY_FILTER: + case ABILITY_SOLID_ROCK: + case ABILITY_PRISM_ARMOR: + if (typeEffectivenessModifier >= UQ_4_12(2.0)) + MulModifier(&finalModifier, UQ_4_12(0.75)); + break; + } + + // target's ally's abilities + if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))) + { + switch (GetBattlerAbility(BATTLE_PARTNER(battlerDef))) + { + case ABILITY_FRIEND_GUARD: + MulModifier(&finalModifier, UQ_4_12(0.75)); + break; + } + } + + // attacker's hold effect + switch (GetBattlerHoldEffect(battlerAtk, TRUE)) + { + case HOLD_EFFECT_METRONOME: + // todo + break; + case HOLD_EFFECT_EXPERT_BELT: + if (typeEffectivenessModifier >= UQ_4_12(2.0)) + MulModifier(&finalModifier, UQ_4_12(1.2)); + break; + case HOLD_EFFECT_LIFE_ORB: + MulModifier(&finalModifier, UQ_4_12(1.3)); + break; + } + + // target's hold effect + switch (GetBattlerHoldEffect(battlerDef, TRUE)) + { + // berries reducing dmg + } + + if (gBattleMoves[move].flags & FLAG_DMG_MINIMIZE && gStatuses3[battlerDef] & STATUS3_MINIMIZED) + MulModifier(&finalModifier, UQ_4_12(2.0)); + if (gBattleMoves[move].flags & FLAG_DMG_UNDERGROUND && gStatuses3[battlerDef] & STATUS3_UNDERGROUND) + MulModifier(&finalModifier, UQ_4_12(2.0)); + if (gBattleMoves[move].flags & FLAG_DMG_UNDERWATER && gStatuses3[battlerDef] & STATUS3_UNDERWATER) + MulModifier(&finalModifier, UQ_4_12(2.0)); + + dmg = ApplyModifier(finalModifier, dmg); + if (dmg == 0) + dmg = 1; + + return dmg; +} + +s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor) +{ + s32 dmg; + u16 finalModifier, typeEffectivenessModifier; + + typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, randomFactor); + + // Don't calculate damage if the move has no effect on target. + if (typeEffectivenessModifier == UQ_4_12(0)) + return 0; + + if (fixedBasePower) + gBattleMovePower = fixedBasePower; + else + gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType); + + // long dmg basic formula + dmg = ((gBattleMons[battlerAtk].level * 2) / 5) + 2; + dmg *= gBattleMovePower; + dmg *= CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit); + dmg /= CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit); + dmg = (dmg / 50) + 2; + + // Calculate final modifiers. + dmg = CalcFinalDmg(dmg, move, battlerAtk, battlerDef, moveType, typeEffectivenessModifier, isCrit); + + // Add a random factor. + if (randomFactor) + { + dmg *= 100 - (Random() % 16); + dmg /= 100; + } + + if (dmg == 0) + dmg = 1; + + return dmg; +} + +static inline void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType) +{ + u16 mod = sTypeEffectivenessTable[moveType][defType]; + + if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT) + mod = UQ_4_12(1.0); + if (moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED) + mod = UQ_4_12(1.0); + if (move == MOVE_FREEZE_DRY && defType == TYPE_WATER) + mod = UQ_4_12(2.0); + + MulModifier(modifier, mod); +} + +u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities) +{ + u16 modifier = UQ_4_12(1.0); + + if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) + { + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1); + if (gBattleMons[battlerDef].type2 != gBattleMons[battlerDef].type1) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type2); + + if (moveType == TYPE_GROUND && !IsBattlerGrounded(battlerDef)) + { + modifier = UQ_4_12(0.0); + if (recordAbilities && GetBattlerAbility(battlerDef) == ABILITY_LEVITATE) + { + gLastUsedAbility = ABILITY_LEVITATE; + gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); + gLastLandedMoves[battlerDef] = 0; + gBattleCommunication[6] = moveType; + RecordAbilityBattle(battlerDef, ABILITY_LEVITATE); + } + } + if (GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gBattleMoves[move].power) + { + modifier = UQ_4_12(0.0); + if (recordAbilities) + { + gLastUsedAbility = ABILITY_WONDER_GUARD; + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastLandedMoves[battlerDef] = 0; + gBattleCommunication[6] = 3; + RecordAbilityBattle(battlerDef, ABILITY_WONDER_GUARD); + } + } + } + + if (modifier == UQ_4_12(0.0)) + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE); + } + else if (modifier == UQ_4_12(1.0)) + { + gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); + } + else if (modifier > UQ_4_12(1.0)) + { + gMoveResultFlags |= MOVE_RESULT_SUPER_EFFECTIVE; + gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); + } + else //if (modifier < UQ_4_12(1.0)) + { + gMoveResultFlags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; + gMoveResultFlags &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); + } + + return modifier; }