diff --git a/data/maps/InsideOfTruck/scripts.inc b/data/maps/InsideOfTruck/scripts.inc index 82a014132..8214611f7 100644 --- a/data/maps/InsideOfTruck/scripts.inc +++ b/data/maps/InsideOfTruck/scripts.inc @@ -50,12 +50,17 @@ InsideOfTruck_EventScript_SetIntroFlagsFemale:: @ 823BF46 end InsideOfTruck_EventScript_MovingBox:: @ 823BF6C - givemon SPECIES_MEW, 55, ITEM_MEWNIUM_Z + givemon SPECIES_STARMIE, 55, ITEM_WATERIUM_Z givemon SPECIES_CHARIZARD, 50, ITEM_FIRIUM_Z - setwildbattle SPECIES_AGGRON, 40, 0 - dowildbattle + + trainerbattle_no_intro TRAINER_VIOLET, sText_test + + @setwildbattle SPECIES_AGGRON, 40, 0 + @dowildbattle @msgbox InsideOfTruck_Text_BoxPrintedWithMonLogo, MSGBOX_SIGN end +sText_test: + .string "hi$" InsideOfTruck_Text_BoxPrintedWithMonLogo: @ 823BF75 .string "The box is printed with a POKéMON logo.\p" diff --git a/include/battle.h b/include/battle.h index 274577622..9844eb2d6 100644 --- a/include/battle.h +++ b/include/battle.h @@ -436,12 +436,12 @@ struct ZMoveData u8 zStatusActive:1; u8 healReplacement:1; //TODO: z-parting shot u8 zUnused:2; - /*0x02*/ u16 currZMove; //z move of cursor / selected z move - /*0x04*/ u16 baseMove; //move turned into z move + /*0x02*/ u16 currZMove; //z move of move cursor is on /*0x06*/ u8 triggerSpriteId; u8 effect; u8 used[MAX_BATTLERS_COUNT]; //one per bank for multi-battles u16 toBeUsed[MAX_BATTLERS_COUNT]; //TODO z moves per battler to be used + u16 baseMoves[MAX_BATTLERS_COUNT]; u8 splits[MAX_BATTLERS_COUNT]; }; /* size = 8 */ diff --git a/include/battle_ai_script_commands.h b/include/battle_ai_script_commands.h index bd990d77a..f8c27bd87 100644 --- a/include/battle_ai_script_commands.h +++ b/include/battle_ai_script_commands.h @@ -23,5 +23,6 @@ void RecordAbilityBattle(u8 battlerId, u8 abilityId); void ClearBattlerAbilityHistory(u8 battlerId); void RecordItemEffectBattle(u8 battlerId, u8 itemEffect); void ClearBattlerItemEffectHistory(u8 battlerId); +bool32 HasMoveWithSplit(u32 battler, u32 split); #endif // GUARD_BATTLE_AI_SCRIPT_COMMANDS_H diff --git a/include/battle_z_move.h b/include/battle_z_move.h index a6c3921e5..70347439a 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -3,6 +3,8 @@ #include "constants/z_move_effects.h" +#define MOVE_Z_STATUS 0xFFFF + struct SignatureZMove { u16 species; @@ -11,6 +13,7 @@ struct SignatureZMove u16 zmove; }; +void QueueZMove(u8 battlerId, u16 baseMove); bool32 IsViableZMove(u8 battlerId, u16 move); bool32 TryChangeZIndicator(u8 battlerId, u16 move); void CreateZMoveTriggerSprite(u8, bool8); @@ -20,5 +23,6 @@ void DestroyZMoveTriggerSprite(void); bool32 MoveSelectionDisplayZMove(u16 zmove); const u8* GetZMoveName(u16 move); void SetZEffect(void); +bool32 ShouldAIUseZMove(u8 activeId, u8 targetId, u16 *baseMove, u8 *chosenMoveId); #endif // GUARD_BATTLE_Z_MOVE_H \ No newline at end of file diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index a5c14023f..0f3047199 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -145,6 +145,9 @@ #define B_INCINERATE_GEMS GEN_6 // In Gen6+, Incinerate can destroy Gems. #define B_MINIMIZE_DMG_ACC GEN_6 // In Gen6+, moves that causes double damage to minimized Pokémon will also skip accuracy checks. +// AI Settings +#define B_AI_PREFER_STATUS_Z_MOVES FALSE // If TRUE, the AI will prefer z-status moves over damaging z moves + // Ability settings #define B_ABILITY_WEATHER GEN_6 // In Gen5+, weather caused by abilities lasts the same amount of turns as induced from a move. Before, they lasted till the battle's end or weather change by a move. #define B_GALE_WINGS GEN_6 // In Gen7+ requires full HP to trigger. diff --git a/include/constants/moves.h b/include/constants/moves.h index 4f00b4d43..f655b8374 100644 --- a/include/constants/moves.h +++ b/include/constants/moves.h @@ -819,6 +819,4 @@ #define MOVES_COUNT MOVES_COUNT_GEN8 #define MOVES_COUNT_Z (MOVE_SOUL_STEALING_7_STAR_STRIKE + 1) -#define MOVE_Z_SIGNATURE 0xFFFF //signature z move - #endif // GUARD_CONSTANTS_MOVES_H diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index 39246c732..9c39c935c 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -2600,7 +2600,7 @@ static u16 *GetMovesArray(u32 battler) return gBattleResources->battleHistory->usedMoves[battler]; } -static bool32 HasMoveWithSplit(u32 battler, u32 split) +bool32 HasMoveWithSplit(u32 battler, u32 split) { s32 i; u16 *moves = GetMovesArray(battler); diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 78765a386..36f05b6a8 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -9,6 +9,7 @@ #include "battle_setup.h" #include "battle_tower.h" #include "battle_tv.h" +#include "battle_z_move.h" #include "bg.h" #include "data.h" #include "frontier_util.h" @@ -1573,18 +1574,34 @@ static void OpponentHandleChooseMove(void) BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget); break; default: - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) - gBattlerTarget = gActiveBattler; - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) { - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + u16 chosenMove = moveInfo->moves[chosenMoveId]; + + if (ShouldAIUseZMove(gActiveBattler, gBattlerTarget, moveInfo->moves, &chosenMoveId)) + { + QueueZMove(gActiveBattler, moveInfo->moves[chosenMoveId]); + chosenMove = moveInfo->moves[chosenMoveId]; + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + else + { + if (gBattleMoves[chosenMove].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + gBattlerTarget = gActiveBattler; + if (gBattleMoves[chosenMove].target & MOVE_TARGET_BOTH) + { + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + } + + if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it. + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); } - if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it. - BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); - else - BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); break; } OpponentBufferExecCompleted(); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index dd7511b20..034c45eb6 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -566,11 +566,7 @@ static void HandleInputChooseMove(void) { u16 chosenMove = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]; - //gBattleStruct->zmove.toBeUsed[gActiveBattler] = gBattleStruct->zmove.currZMove; - - gBattleStruct->zmove.baseMove = chosenMove; - gBattleStruct->zmove.split = GetBattleMoveSplit(chosenMove); - gBattleStruct->zmove.active = TRUE; + QueueZMove(gActiveBattler, chosenMove); gBattleStruct->zmove.viewing = FALSE; if (gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].split != SPLIT_STATUS) moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index fc4ad5c90..a40cad90b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5099,7 +5099,10 @@ static void Cmd_moveend(void) gSpecialStatuses[gBattlerAttacker].damagedMons = 0; gSpecialStatuses[gBattlerTarget].berryReduced = 0; gBattleScripting.moveEffect = 0; + // clear attacker z move data gBattleStruct->zmove.active = FALSE; + gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE; + gBattleStruct->zmove.effect = EFFECT_HIT; gBattleScripting.moveendState++; break; case MOVEEND_COUNT: diff --git a/src/battle_util.c b/src/battle_util.c index 26f30d3c0..3f856e23b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -124,9 +124,9 @@ void HandleAction_UseMove(void) } // check z move used - if (gBattleStruct->zmove.active) + if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker]) { - gCurrentMove = gBattleStruct->zmove.currZMove; + gCurrentMove = gBattleStruct->zmove.toBeUsed[gBattlerAttacker]; } if (gBattleMons[gBattlerAttacker].hp != 0) @@ -3187,16 +3187,17 @@ u8 AtkCanceller_UnableToUseMove(void) gBattleStruct->atkCancellerTracker++; break; case CANCELLER_Z_MOVES: - if (gBattleStruct->zmove.active == TRUE) + if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE) { + //attacker has a queued z move + gBattleStruct->zmove.active = TRUE; RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL); gBattleStruct->zmove.used[gBattlerAttacker] = TRUE; - //TODO - partner battles. + //TODO - partner battles gBattleScripting.battler = gBattlerAttacker; - - if (IS_MOVE_STATUS(gBattleStruct->zmove.baseMove)) + if (IS_MOVE_STATUS(gBattleStruct->zmove.splits[gBattlerAttacker])) { - gBattleStruct->zmove.effect = gBattleMoves[gBattleStruct->zmove.baseMove].zMoveEffect; + gBattleStruct->zmove.effect = gBattleMoves[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMoveEffect; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ZMoveActivateStatus; } @@ -6402,7 +6403,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) u32 weight, hpFraction, speed; if (gBattleStruct->zmove.active) - return gBattleMoves[gBattleStruct->zmove.baseMove].zMovePower; + return gBattleMoves[gBattleStruct->zmove.baseMoves[battlerAtk]].zMovePower; switch (gBattleMoves[move].effect) { @@ -7589,6 +7590,9 @@ bool32 CanMegaEvolve(u8 battlerId) // Check if trainer already mega evolved a pokemon. if (mega->alreadyEvolved[battlerPosition]) return FALSE; + if (gBattleStruct->zmove.toBeUsed[battlerId]) + return FALSE; //cannot use z move and mega evolve on same turn + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (IsPartnerMonFromSameTrainer(battlerId) diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 36cd326ea..e8dc7210b 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -2,6 +2,7 @@ #include "malloc.h" #include "battle.h" #include "pokemon.h" +#include "battle_ai_script_commands.h" #include "battle_controllers.h" #include "battle_interface.h" #include "battle_message.h" @@ -38,6 +39,10 @@ #include "constants/hold_effects.h" #include "constants/battle_string_ids.h" #include "constants/battle_move_effects.h" +#include "constants/abilities.h" +#include "constants/moves.h" + +#define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1]) // Function Declarations static void SpriteCB_ZMoveTrigger(struct Sprite *sprite); @@ -46,7 +51,8 @@ static u16 GetTypeBasedZMove(u16 move, u8 battler); static void ZMoveSelectionDisplayPpNumber(void); static void ZMoveSelectionDisplayPower(u16 move, u16 zMove); static void ShowZMoveTriggerSprite(void); -static bool32 AreMainStatsMaxed(u8 battlerId); +static bool32 AreStatsMaxed(u8 battlerId, u8 n); +static u8 GetZMoveScore(u8 battlerAtk, u8 battlerDef, u16 baseMove, u16 zMove); // Const Data static const struct SignatureZMove sSignatureZMoves[] = @@ -132,6 +138,13 @@ bool8 IsZMove(u16 move) return move >= FIRST_Z_MOVE && move <= LAST_Z_MOVE; } +void QueueZMove(u8 battlerId, u16 baseMove) +{ + gBattleStruct->zmove.toBeUsed[battlerId] = gBattleStruct->zmove.currZMove; + gBattleStruct->zmove.baseMoves[battlerId] = baseMove; + gBattleStruct->zmove.splits[battlerId] = GetBattleMoveSplit(baseMove); +} + bool32 IsViableZMove(u8 battlerId, u16 move) { struct Pokemon *mon; @@ -169,8 +182,7 @@ bool32 IsViableZMove(u8 battlerId, u16 move) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { - if (IsPartnerMonFromSameTrainer(battlerId) - && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) + if (IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) return FALSE; //partner has mega evolved or is about to mega evolve } @@ -187,7 +199,6 @@ bool32 IsViableZMove(u8 battlerId, u16 move) if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) #endif { - //TODO: status z moves u16 zMove = GetSignatureZMove(move, gBattleMons[battlerId].species, item); if (zMove != MOVE_NONE) { @@ -195,10 +206,10 @@ bool32 IsViableZMove(u8 battlerId, u16 move) return TRUE; } - if (move != MOVE_NONE && zMove != MOVE_Z_SIGNATURE && gBattleMoves[move].type == ItemId_GetSecondaryId(item)) + if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gBattleMoves[move].type == ItemId_GetSecondaryId(item)) { - if (gBattleMoves[move].split == SPLIT_STATUS) - gBattleStruct->zmove.currZMove = MOVE_Z_SIGNATURE; + if (IS_MOVE_STATUS(gBattleMoves[move].split)) + gBattleStruct->zmove.currZMove = MOVE_Z_STATUS; else gBattleStruct->zmove.currZMove = GetTypeBasedZMove(move, battlerId); @@ -392,7 +403,7 @@ bool32 MoveSelectionDisplayZMove(u16 zmove) BattlePutTextOnWindow(gDisplayedStringBattle, i + 3); } - //if (zmove == MOVE_Z_SIGNATURE) + //if (zmove == MOVE_Z_STATUS) if (IS_MOVE_STATUS(move)) { u8 zEffect = gBattleMoves[move].zMoveEffect; @@ -500,18 +511,18 @@ bool32 MoveSelectionDisplayZMove(u16 zmove) static void ZMoveSelectionDisplayPower(u16 move, u16 zMove) { - u8 *txtPtr; - u16 power = gBattleMoves[move].zMovePower; + u8 *txtPtr; + u16 power = gBattleMoves[move].zMovePower; - if (zMove >= MOVE_CATASTROPIKA) - power = gBattleMoves[zMove].power; + if (zMove >= MOVE_CATASTROPIKA) + power = gBattleMoves[zMove].power; - if (gBattleMoves[move].split != SPLIT_STATUS) - { - txtPtr = StringCopy(gDisplayedStringBattle, sText_PowerColon); - ConvertIntToDecimalStringN(txtPtr, power, STR_CONV_MODE_LEFT_ALIGN, 3); - BattlePutTextOnWindow(gDisplayedStringBattle, 5); //bottom left - } + if (gBattleMoves[move].split != SPLIT_STATUS) + { + txtPtr = StringCopy(gDisplayedStringBattle, sText_PowerColon); + ConvertIntToDecimalStringN(txtPtr, power, STR_CONV_MODE_LEFT_ALIGN, 3); + BattlePutTextOnWindow(gDisplayedStringBattle, 5); //bottom left + } } static void ZMoveSelectionDisplayPpNumber(void) @@ -568,7 +579,7 @@ void SetZEffect(void) gBattlescriptCurrInstr = BattleScript_ZEffectPrintString - Z_EFFECT_BS_LENGTH; break; case Z_EFFECT_ALL_STATS_UP_1: - if (!AreMainStatsMaxed(gBattlerAttacker)) + if (!AreStatsMaxed(gBattlerAttacker, STAT_SPDEF)) { for (i = 0; i < STAT_ACC - 1; i++) //Doesn't increase Acc or Evsn { @@ -661,15 +672,270 @@ void SetZEffect(void) gBattleStruct->zmove.zStatusActive = FALSE; } -#define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1]) -static bool32 AreMainStatsMaxed(u8 battlerId) +static bool32 AreStatsMaxed(u8 battlerId, u8 n) { u32 i; - for (i = STAT_ATK; i <= STAT_SPDEF; i++) - { - if (STAT_STAGE(battlerId, i) < MAX_STAT_STAGE) - return FALSE; - } - return TRUE; + for (i = STAT_ATK; i <= n; i++) + { + if (STAT_STAGE(battlerId, i) < MAX_STAT_STAGE) + return FALSE; + } + return TRUE; +} + +#define STAT_CAN_RISE(bank, stat) ((gBattleMons[bank].statStages[stat-1] < 12 && GetBattlerAbility(bank) != ABILITY_CONTRARY) || (GetBattlerAbility(bank) == ABILITY_CONTRARY && gBattleMons[bank].statStages[stat-1] > 0)) +#define STAT_CAN_FALL(bank, stat) ((gBattleMons[bank].statStages[stat-1] > 0 && GetBattlerAbility(bank) != ABILITY_CONTRARY) || (GetBattlerAbility(bank) == ABILITY_CONTRARY && gBattleMons[bank].statStages[stat-1] < 12)) +//TODO - this could be a lot better +bool32 ShouldAIUseZMove(u8 battlerAtk, u8 battlerDef, u16 *baseMoves, u8 *chosenMoveId) +{ + u32 i; + u16 possibleZMoves[MAX_MON_MOVES]; + u8 zMoveIndex = 0xFF; + u16 zMove = MOVE_NONE; + u8 scores[MAX_MON_MOVES] = {0}; + + if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && battlerDef == BATTLE_PARTNER(battlerAtk)) + return FALSE; //don't use z move on partner + + if (gBattleStruct->zmove.used[battlerAtk]) + return FALSE; //cant use z move twice + + // check possible z moves and select the 'best' one + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (baseMoves[i] != MOVE_NONE && IsViableZMove(battlerAtk, baseMoves[i])) //updates gBattleStruct->zmove.currZMove + { + if (zMove != MOVE_NONE) + { + scores[i] = GetZMoveScore(battlerAtk, battlerDef, baseMoves[i], zMove); + // another z move option already exists. compare them + #if B_AI_PREFER_STATUS_Z_MOVES == TRUE + if (scores[i] > scores[zMoveIndex] || (IS_MOVE_STATUS(gBattleStruct->zmove.currZMove) && !IS_MOVE_STATUS(zMove) && scores[i] != 0)) + { + zMove = gBattleStruct->zmove.currZMove; + zMoveIndex = i; + } + #else + if (scores[i] > scores[zMoveIndex] || (!IS_MOVE_STATUS(gBattleStruct->zmove.currZMove) && IS_MOVE_STATUS(zMove) && scores[i] != 0)) + { + zMove = gBattleStruct->zmove.currZMove; + zMoveIndex = i; + } + #endif + } + else + { + zMove = gBattleStruct->zmove.currZMove; + zMoveIndex = i; + } + } + } + + if (zMoveIndex == 0xFF || scores[zMoveIndex] == 0) + { + return FALSE; //no available z moves + } + else + { + *chosenMoveId = zMoveIndex; + gBattleStruct->zmove.baseMoves[battlerAtk] = baseMoves[*chosenMoveId]; + gBattleStruct->zmove.currZMove = zMove; //z move the AI is looking at + return TRUE; + } + + return FALSE; +} + +static u8 GetZMoveScore(u8 battlerAtk, u8 battlerDef, u16 baseMove, u16 zMove) +{ + u8 score = 0; + u8 expectedDamage = 0; + u32 i; + + if (zMove != MOVE_Z_STATUS) + { + u8 defAbility = GetBattlerAbility(battlerDef); + u16 defSpecies = gBattleMons[battlerDef].species; + + /*if (baseMove == MOVE_FAKE_OUT && gDisableStructs[battlerAtk].isFirstTurn) + return FALSE; //Prefer actual Fake Out over Breakneck Blitz*/ + + /*if (MoveBlockedBySubstitute(zMove, battlerAtk, battlerDef) + || (defMovePrediction == MOVE_SUBSTITUTE + && !MoveWouldHitFirst(zMove, battlerAtk, battlerDef) + && !MoveIgnoresSubstitutes(zMove, ABILITY(battlerAtk)))) + return FALSE; //Don't use a Z-Move on a Substitute or if the enemy is going to go first and use Substitute*/ + + #ifdef POKEMON_EXPANSION + if (defAbility == ABILITY_DISGUISE && defSpecies == SPECIES_MIMIKYU) + return 0; //Don't waste a Z-Move busting Mimikyu's disguise + if (defAbility == ABILITY_ICEFACE && defSpecies == SPECIES_EISCUE && IS_MOVE_PHYSICAL(baseMove)) + return 0; //Don't waste a Z-Move busting Eiscue's Ice Face + #endif + + /*if (defMovePrediction == MOVE_PROTECT || defMovePrediction == MOVE_KINGSSHIELD || defMovePrediction == MOVE_SPIKYSHIELD || defMovePrediction == MOVE_OBSTRUCT + || (IsDynamaxed(battlerDef) && SPLIT(defMovePrediction) == SPLIT_STATUS)) + return FALSE; //Don't waste a Z-Move on a Protect*/ + + /*if (IsRaidBattle() && gNewBS->dynamaxData.raidShieldsUp && SIDE(battlerAtk) == B_SIDE_PLAYER && SIDE(battlerDef) == B_SIDE_OPPONENT) //Partner AI on Raid Pokemon with shields up + { + if (gNewBS->dynamaxData.shieldCount - gNewBS->dynamaxData.shieldsDestroyed <= 2 //Less than 3 shields left + && gNewBS->dynamaxData.stormLevel < 3) //The Raid boss hasn't almost won + return FALSE; //Don't waste a Z-Move breaking a shield + + u16 bankAtkPartner = BATTLE_PARTNER(battlerAtk); + u16 partnerMove = GetAIChosenMove(bankAtkPartner, battlerDef); + + if (SPLIT(partnerMove) == SPLIT_STATUS + || MoveWouldHitFirst(partnerMove, bankAtkPartner, battlerAtk) + || (gChosenMovesByBanks[bankAtkPartner] != MOVE_NONE && gBattleStruct->moveTarget[bankAtkPartner] != battlerDef)) //Not targeting raid Pokemon + return FALSE; //Don't waste a Z-Move if partner can't destroy shield first + }*/ + + //These moves should always be turned into Z-Moves, regardless if they KO or not + switch (gBattleMoves[baseMove].effect) + { + case EFFECT_RECHARGE: + case EFFECT_SEMI_INVULNERABLE: + case EFFECT_SKULL_BASH: + case EFFECT_SOLARBEAM: + case EFFECT_LAST_RESORT: + //todo: sky drop + return 255; + } + if (baseMove == MOVE_SKY_ATTACK) + return 255; + + gBattleStruct->zmove.active = TRUE; //for damage calc only + expectedDamage = AI_CalcDamage(baseMove, battlerAtk, battlerDef); + gBattleStruct->zmove.active = FALSE; + if (expectedDamage >= gBattleMons[battlerDef].hp) + return 0; //base move knocks out already, no need for z move + + return (expectedDamage > 255) ? 255 : expectedDamage; + } + else //Status Move + { + u8 zEffect = gBattleMoves[baseMove].zMoveEffect; + + switch (zEffect) + { + case Z_EFFECT_NONE: + return 0; + case Z_EFFECT_RESET_STATS: + for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) + { + if (i == STAT_ATK && !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) //Only reset lowered Attack if useful + continue; + else if (i == STAT_ATK && !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) //Only reset lowered Special Attack if useful + continue; + + if (STAT_STAGE(battlerAtk, i) < 6) + return 50; //Want to reset any negative stats + } + break; + case Z_EFFECT_ALL_STATS_UP_1: + if (!AreStatsMaxed(battlerAtk, STAT_EVASION)) //all battle stats maxed + return 80; + break; + case Z_EFFECT_BOOST_CRITS: + if (!(gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY)) + return 30; //kinda meh? + break; + case Z_EFFECT_FOLLOW_ME: + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + return 30; //kinda meh? + break; + case Z_EFFECT_ATK_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_ATK) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + return 60; + break; + case Z_EFFECT_ATK_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_ATK) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + return 70; + break; + case Z_EFFECT_ATK_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_ATK) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + return 80; + break; + case Z_EFFECT_DEF_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_DEF)) + return 50; + break; + case Z_EFFECT_DEF_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_DEF)) + return 60; + break; + case Z_EFFECT_DEF_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_DEF)) + return 70; + break; + case Z_EFFECT_SPATK_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_SPATK) && HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + return 50; + break; + case Z_EFFECT_SPATK_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_SPATK) && HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + return 60; + break; + case Z_EFFECT_SPATK_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_SPATK) && HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + return 70; + break; + case Z_EFFECT_SPDEF_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_SPDEF)) + return 50; + break; + case Z_EFFECT_SPDEF_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_SPDEF)) + return 60; + break; + case Z_EFFECT_SPDEF_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_SPDEF)) + return 70; + break; + case Z_EFFECT_SPD_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_SPEED)) + return 50; + break; + case Z_EFFECT_SPD_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_SPEED)) + return 60; + break; + case Z_EFFECT_SPD_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_SPEED)) + return 70; + break; + case Z_EFFECT_ACC_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_ACC)) + return 20; //TODO: only if knows low-accuracy move + break; + case Z_EFFECT_ACC_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_ACC)) + return 40; //TODO: only if knows low-accuracy move + break; + case Z_EFFECT_ACC_UP_3: + //if (STAT_CAN_RISE(battlerAtk, STAT_ACC) && MoveInMovesetWithAccuracyLessThan(battlerAtk, battlerDef, 90, FALSE)) + if (STAT_CAN_RISE(battlerAtk, STAT_ACC)) + return 60; //TODO: only if knows low-accuracy move + break; + case Z_EFFECT_EVSN_UP_1: + if (STAT_CAN_RISE(battlerAtk, STAT_EVASION)) + return 40; + break; + case Z_EFFECT_EVSN_UP_2: + if (STAT_CAN_RISE(battlerAtk, STAT_EVASION)) + return 60; + break; + case Z_EFFECT_EVSN_UP_3: + if (STAT_CAN_RISE(battlerAtk, STAT_EVASION)) + return 80; + break; + default: //Recover HP + return 70; + } + } + + return score; } diff --git a/src/data/pokemon/level_up_learnsets.h b/src/data/pokemon/level_up_learnsets.h index 69742ac25..6b76f7b9c 100644 --- a/src/data/pokemon/level_up_learnsets.h +++ b/src/data/pokemon/level_up_learnsets.h @@ -2385,6 +2385,8 @@ static const struct LevelUpMove sStarmieLevelUpLearnset[] = { LEVEL_UP_MOVE( 1, MOVE_RECOVER), LEVEL_UP_MOVE( 1, MOVE_SWIFT), LEVEL_UP_MOVE(40, MOVE_CONFUSE_RAY), + LEVEL_UP_MOVE(40, MOVE_PSYCHIC), + LEVEL_UP_MOVE(50, MOVE_WATER_PULSE), LEVEL_UP_END }; diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index 824a49dc3..2c57f6ffc 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -429,16 +429,18 @@ static const struct TrainerMonNoItemCustomMoves sParty_Felix[] = { } }; -static const struct TrainerMonNoItemDefaultMoves sParty_Violet[] = { +static const struct TrainerMonItemDefaultMoves sParty_Violet[] = { { .iv = 0, - .lvl = 26, + .lvl = 45, .species = SPECIES_ROSELIA, + .heldItem = ITEM_GRASSIUM_Z, }, { .iv = 0, - .lvl = 26, + .lvl = 40, .species = SPECIES_GLOOM, + .heldItem = 0, } }; diff --git a/src/data/trainers.h b/src/data/trainers.h index 163a8296d..defe0accf 100644 --- a/src/data/trainers.h +++ b/src/data/trainers.h @@ -547,7 +547,7 @@ const struct Trainer gTrainers[] = { [TRAINER_VIOLET] = { - .partyFlags = 0, + .partyFlags = F_TRAINER_PARTY_HELD_ITEM, .trainerClass = TRAINER_CLASS_AROMA_LADY, .encounterMusic_gender = F_TRAINER_FEMALE | TRAINER_ENCOUNTER_MUSIC_FEMALE, .trainerPic = TRAINER_PIC_AROMA_LADY, @@ -556,7 +556,7 @@ const struct Trainer gTrainers[] = { .doubleBattle = FALSE, .aiFlags = AI_SCRIPT_CHECK_BAD_MOVE, .partySize = ARRAY_COUNT(sParty_Violet), - .party = {.NoItemDefaultMoves = sParty_Violet}, + .party = {.ItemDefaultMoves = sParty_Violet}, }, [TRAINER_ROSE_2] =