mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 19:47:35 +01:00
trainer z move ai
This commit is contained in:
parent
a0bde70493
commit
fd8951d040
@ -50,12 +50,17 @@ InsideOfTruck_EventScript_SetIntroFlagsFemale:: @ 823BF46
|
|||||||
end
|
end
|
||||||
|
|
||||||
InsideOfTruck_EventScript_MovingBox:: @ 823BF6C
|
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
|
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
|
@msgbox InsideOfTruck_Text_BoxPrintedWithMonLogo, MSGBOX_SIGN
|
||||||
end
|
end
|
||||||
|
sText_test:
|
||||||
|
.string "hi$"
|
||||||
|
|
||||||
InsideOfTruck_Text_BoxPrintedWithMonLogo: @ 823BF75
|
InsideOfTruck_Text_BoxPrintedWithMonLogo: @ 823BF75
|
||||||
.string "The box is printed with a POKéMON logo.\p"
|
.string "The box is printed with a POKéMON logo.\p"
|
||||||
|
@ -436,12 +436,12 @@ struct ZMoveData
|
|||||||
u8 zStatusActive:1;
|
u8 zStatusActive:1;
|
||||||
u8 healReplacement:1; //TODO: z-parting shot
|
u8 healReplacement:1; //TODO: z-parting shot
|
||||||
u8 zUnused:2;
|
u8 zUnused:2;
|
||||||
/*0x02*/ u16 currZMove; //z move of cursor / selected z move
|
/*0x02*/ u16 currZMove; //z move of move cursor is on
|
||||||
/*0x04*/ u16 baseMove; //move turned into z move
|
|
||||||
/*0x06*/ u8 triggerSpriteId;
|
/*0x06*/ u8 triggerSpriteId;
|
||||||
u8 effect;
|
u8 effect;
|
||||||
u8 used[MAX_BATTLERS_COUNT]; //one per bank for multi-battles
|
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 toBeUsed[MAX_BATTLERS_COUNT]; //TODO z moves per battler to be used
|
||||||
|
u16 baseMoves[MAX_BATTLERS_COUNT];
|
||||||
u8 splits[MAX_BATTLERS_COUNT];
|
u8 splits[MAX_BATTLERS_COUNT];
|
||||||
}; /* size = 8 */
|
}; /* size = 8 */
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ void RecordAbilityBattle(u8 battlerId, u8 abilityId);
|
|||||||
void ClearBattlerAbilityHistory(u8 battlerId);
|
void ClearBattlerAbilityHistory(u8 battlerId);
|
||||||
void RecordItemEffectBattle(u8 battlerId, u8 itemEffect);
|
void RecordItemEffectBattle(u8 battlerId, u8 itemEffect);
|
||||||
void ClearBattlerItemEffectHistory(u8 battlerId);
|
void ClearBattlerItemEffectHistory(u8 battlerId);
|
||||||
|
bool32 HasMoveWithSplit(u32 battler, u32 split);
|
||||||
|
|
||||||
#endif // GUARD_BATTLE_AI_SCRIPT_COMMANDS_H
|
#endif // GUARD_BATTLE_AI_SCRIPT_COMMANDS_H
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "constants/z_move_effects.h"
|
#include "constants/z_move_effects.h"
|
||||||
|
|
||||||
|
#define MOVE_Z_STATUS 0xFFFF
|
||||||
|
|
||||||
struct SignatureZMove
|
struct SignatureZMove
|
||||||
{
|
{
|
||||||
u16 species;
|
u16 species;
|
||||||
@ -11,6 +13,7 @@ struct SignatureZMove
|
|||||||
u16 zmove;
|
u16 zmove;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void QueueZMove(u8 battlerId, u16 baseMove);
|
||||||
bool32 IsViableZMove(u8 battlerId, u16 move);
|
bool32 IsViableZMove(u8 battlerId, u16 move);
|
||||||
bool32 TryChangeZIndicator(u8 battlerId, u16 move);
|
bool32 TryChangeZIndicator(u8 battlerId, u16 move);
|
||||||
void CreateZMoveTriggerSprite(u8, bool8);
|
void CreateZMoveTriggerSprite(u8, bool8);
|
||||||
@ -20,5 +23,6 @@ void DestroyZMoveTriggerSprite(void);
|
|||||||
bool32 MoveSelectionDisplayZMove(u16 zmove);
|
bool32 MoveSelectionDisplayZMove(u16 zmove);
|
||||||
const u8* GetZMoveName(u16 move);
|
const u8* GetZMoveName(u16 move);
|
||||||
void SetZEffect(void);
|
void SetZEffect(void);
|
||||||
|
bool32 ShouldAIUseZMove(u8 activeId, u8 targetId, u16 *baseMove, u8 *chosenMoveId);
|
||||||
|
|
||||||
#endif // GUARD_BATTLE_Z_MOVE_H
|
#endif // GUARD_BATTLE_Z_MOVE_H
|
@ -145,6 +145,9 @@
|
|||||||
#define B_INCINERATE_GEMS GEN_6 // In Gen6+, Incinerate can destroy Gems.
|
#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.
|
#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
|
// 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_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.
|
#define B_GALE_WINGS GEN_6 // In Gen7+ requires full HP to trigger.
|
||||||
|
@ -819,6 +819,4 @@
|
|||||||
#define MOVES_COUNT MOVES_COUNT_GEN8
|
#define MOVES_COUNT MOVES_COUNT_GEN8
|
||||||
#define MOVES_COUNT_Z (MOVE_SOUL_STEALING_7_STAR_STRIKE + 1)
|
#define MOVES_COUNT_Z (MOVE_SOUL_STEALING_7_STAR_STRIKE + 1)
|
||||||
|
|
||||||
#define MOVE_Z_SIGNATURE 0xFFFF //signature z move
|
|
||||||
|
|
||||||
#endif // GUARD_CONSTANTS_MOVES_H
|
#endif // GUARD_CONSTANTS_MOVES_H
|
||||||
|
@ -2600,7 +2600,7 @@ static u16 *GetMovesArray(u32 battler)
|
|||||||
return gBattleResources->battleHistory->usedMoves[battler];
|
return gBattleResources->battleHistory->usedMoves[battler];
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool32 HasMoveWithSplit(u32 battler, u32 split)
|
bool32 HasMoveWithSplit(u32 battler, u32 split)
|
||||||
{
|
{
|
||||||
s32 i;
|
s32 i;
|
||||||
u16 *moves = GetMovesArray(battler);
|
u16 *moves = GetMovesArray(battler);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "battle_setup.h"
|
#include "battle_setup.h"
|
||||||
#include "battle_tower.h"
|
#include "battle_tower.h"
|
||||||
#include "battle_tv.h"
|
#include "battle_tv.h"
|
||||||
|
#include "battle_z_move.h"
|
||||||
#include "bg.h"
|
#include "bg.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "frontier_util.h"
|
#include "frontier_util.h"
|
||||||
@ -1573,18 +1574,34 @@ static void OpponentHandleChooseMove(void)
|
|||||||
BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget);
|
BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
{
|
||||||
|
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;
|
gBattlerTarget = gActiveBattler;
|
||||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
|
if (gBattleMoves[chosenMove].target & MOVE_TARGET_BOTH)
|
||||||
{
|
{
|
||||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it.
|
if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it.
|
||||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||||
else
|
else
|
||||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
OpponentBufferExecCompleted();
|
OpponentBufferExecCompleted();
|
||||||
|
@ -566,11 +566,7 @@ static void HandleInputChooseMove(void)
|
|||||||
{
|
{
|
||||||
u16 chosenMove = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]];
|
u16 chosenMove = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]];
|
||||||
|
|
||||||
//gBattleStruct->zmove.toBeUsed[gActiveBattler] = gBattleStruct->zmove.currZMove;
|
QueueZMove(gActiveBattler, chosenMove);
|
||||||
|
|
||||||
gBattleStruct->zmove.baseMove = chosenMove;
|
|
||||||
gBattleStruct->zmove.split = GetBattleMoveSplit(chosenMove);
|
|
||||||
gBattleStruct->zmove.active = TRUE;
|
|
||||||
gBattleStruct->zmove.viewing = FALSE;
|
gBattleStruct->zmove.viewing = FALSE;
|
||||||
if (gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].split != SPLIT_STATUS)
|
if (gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].split != SPLIT_STATUS)
|
||||||
moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target
|
moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target
|
||||||
|
@ -5099,7 +5099,10 @@ static void Cmd_moveend(void)
|
|||||||
gSpecialStatuses[gBattlerAttacker].damagedMons = 0;
|
gSpecialStatuses[gBattlerAttacker].damagedMons = 0;
|
||||||
gSpecialStatuses[gBattlerTarget].berryReduced = 0;
|
gSpecialStatuses[gBattlerTarget].berryReduced = 0;
|
||||||
gBattleScripting.moveEffect = 0;
|
gBattleScripting.moveEffect = 0;
|
||||||
|
// clear attacker z move data
|
||||||
gBattleStruct->zmove.active = FALSE;
|
gBattleStruct->zmove.active = FALSE;
|
||||||
|
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
|
||||||
|
gBattleStruct->zmove.effect = EFFECT_HIT;
|
||||||
gBattleScripting.moveendState++;
|
gBattleScripting.moveendState++;
|
||||||
break;
|
break;
|
||||||
case MOVEEND_COUNT:
|
case MOVEEND_COUNT:
|
||||||
|
@ -124,9 +124,9 @@ void HandleAction_UseMove(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check z move used
|
// 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)
|
if (gBattleMons[gBattlerAttacker].hp != 0)
|
||||||
@ -3187,16 +3187,17 @@ u8 AtkCanceller_UnableToUseMove(void)
|
|||||||
gBattleStruct->atkCancellerTracker++;
|
gBattleStruct->atkCancellerTracker++;
|
||||||
break;
|
break;
|
||||||
case CANCELLER_Z_MOVES:
|
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);
|
RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL);
|
||||||
gBattleStruct->zmove.used[gBattlerAttacker] = TRUE;
|
gBattleStruct->zmove.used[gBattlerAttacker] = TRUE;
|
||||||
//TODO - partner battles.
|
//TODO - partner battles
|
||||||
gBattleScripting.battler = gBattlerAttacker;
|
gBattleScripting.battler = gBattlerAttacker;
|
||||||
|
if (IS_MOVE_STATUS(gBattleStruct->zmove.splits[gBattlerAttacker]))
|
||||||
if (IS_MOVE_STATUS(gBattleStruct->zmove.baseMove))
|
|
||||||
{
|
{
|
||||||
gBattleStruct->zmove.effect = gBattleMoves[gBattleStruct->zmove.baseMove].zMoveEffect;
|
gBattleStruct->zmove.effect = gBattleMoves[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMoveEffect;
|
||||||
BattleScriptPushCursor();
|
BattleScriptPushCursor();
|
||||||
gBattlescriptCurrInstr = BattleScript_ZMoveActivateStatus;
|
gBattlescriptCurrInstr = BattleScript_ZMoveActivateStatus;
|
||||||
}
|
}
|
||||||
@ -6402,7 +6403,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||||||
u32 weight, hpFraction, speed;
|
u32 weight, hpFraction, speed;
|
||||||
|
|
||||||
if (gBattleStruct->zmove.active)
|
if (gBattleStruct->zmove.active)
|
||||||
return gBattleMoves[gBattleStruct->zmove.baseMove].zMovePower;
|
return gBattleMoves[gBattleStruct->zmove.baseMoves[battlerAtk]].zMovePower;
|
||||||
|
|
||||||
switch (gBattleMoves[move].effect)
|
switch (gBattleMoves[move].effect)
|
||||||
{
|
{
|
||||||
@ -7589,6 +7590,9 @@ bool32 CanMegaEvolve(u8 battlerId)
|
|||||||
// Check if trainer already mega evolved a pokemon.
|
// Check if trainer already mega evolved a pokemon.
|
||||||
if (mega->alreadyEvolved[battlerPosition])
|
if (mega->alreadyEvolved[battlerPosition])
|
||||||
return FALSE;
|
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 (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||||
{
|
{
|
||||||
if (IsPartnerMonFromSameTrainer(battlerId)
|
if (IsPartnerMonFromSameTrainer(battlerId)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
#include "battle.h"
|
#include "battle.h"
|
||||||
#include "pokemon.h"
|
#include "pokemon.h"
|
||||||
|
#include "battle_ai_script_commands.h"
|
||||||
#include "battle_controllers.h"
|
#include "battle_controllers.h"
|
||||||
#include "battle_interface.h"
|
#include "battle_interface.h"
|
||||||
#include "battle_message.h"
|
#include "battle_message.h"
|
||||||
@ -38,6 +39,10 @@
|
|||||||
#include "constants/hold_effects.h"
|
#include "constants/hold_effects.h"
|
||||||
#include "constants/battle_string_ids.h"
|
#include "constants/battle_string_ids.h"
|
||||||
#include "constants/battle_move_effects.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
|
// Function Declarations
|
||||||
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite);
|
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite);
|
||||||
@ -46,7 +51,8 @@ static u16 GetTypeBasedZMove(u16 move, u8 battler);
|
|||||||
static void ZMoveSelectionDisplayPpNumber(void);
|
static void ZMoveSelectionDisplayPpNumber(void);
|
||||||
static void ZMoveSelectionDisplayPower(u16 move, u16 zMove);
|
static void ZMoveSelectionDisplayPower(u16 move, u16 zMove);
|
||||||
static void ShowZMoveTriggerSprite(void);
|
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
|
// Const Data
|
||||||
static const struct SignatureZMove sSignatureZMoves[] =
|
static const struct SignatureZMove sSignatureZMoves[] =
|
||||||
@ -132,6 +138,13 @@ bool8 IsZMove(u16 move)
|
|||||||
return move >= FIRST_Z_MOVE && move <= LAST_Z_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)
|
bool32 IsViableZMove(u8 battlerId, u16 move)
|
||||||
{
|
{
|
||||||
struct Pokemon *mon;
|
struct Pokemon *mon;
|
||||||
@ -169,8 +182,7 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
|
|||||||
|
|
||||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||||
{
|
{
|
||||||
if (IsPartnerMonFromSameTrainer(battlerId)
|
if (IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)])))
|
||||||
&& (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)])))
|
|
||||||
return FALSE; //partner has mega evolved or is about to mega evolve
|
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)
|
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
//TODO: status z moves
|
|
||||||
u16 zMove = GetSignatureZMove(move, gBattleMons[battlerId].species, item);
|
u16 zMove = GetSignatureZMove(move, gBattleMons[battlerId].species, item);
|
||||||
if (zMove != MOVE_NONE)
|
if (zMove != MOVE_NONE)
|
||||||
{
|
{
|
||||||
@ -195,10 +206,10 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
|
|||||||
return TRUE;
|
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)
|
if (IS_MOVE_STATUS(gBattleMoves[move].split))
|
||||||
gBattleStruct->zmove.currZMove = MOVE_Z_SIGNATURE;
|
gBattleStruct->zmove.currZMove = MOVE_Z_STATUS;
|
||||||
else
|
else
|
||||||
gBattleStruct->zmove.currZMove = GetTypeBasedZMove(move, battlerId);
|
gBattleStruct->zmove.currZMove = GetTypeBasedZMove(move, battlerId);
|
||||||
|
|
||||||
@ -392,7 +403,7 @@ bool32 MoveSelectionDisplayZMove(u16 zmove)
|
|||||||
BattlePutTextOnWindow(gDisplayedStringBattle, i + 3);
|
BattlePutTextOnWindow(gDisplayedStringBattle, i + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (zmove == MOVE_Z_SIGNATURE)
|
//if (zmove == MOVE_Z_STATUS)
|
||||||
if (IS_MOVE_STATUS(move))
|
if (IS_MOVE_STATUS(move))
|
||||||
{
|
{
|
||||||
u8 zEffect = gBattleMoves[move].zMoveEffect;
|
u8 zEffect = gBattleMoves[move].zMoveEffect;
|
||||||
@ -568,7 +579,7 @@ void SetZEffect(void)
|
|||||||
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString - Z_EFFECT_BS_LENGTH;
|
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString - Z_EFFECT_BS_LENGTH;
|
||||||
break;
|
break;
|
||||||
case Z_EFFECT_ALL_STATS_UP_1:
|
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
|
for (i = 0; i < STAT_ACC - 1; i++) //Doesn't increase Acc or Evsn
|
||||||
{
|
{
|
||||||
@ -661,11 +672,10 @@ void SetZEffect(void)
|
|||||||
gBattleStruct->zmove.zStatusActive = FALSE;
|
gBattleStruct->zmove.zStatusActive = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1])
|
static bool32 AreStatsMaxed(u8 battlerId, u8 n)
|
||||||
static bool32 AreMainStatsMaxed(u8 battlerId)
|
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
for (i = STAT_ATK; i <= STAT_SPDEF; i++)
|
for (i = STAT_ATK; i <= n; i++)
|
||||||
{
|
{
|
||||||
if (STAT_STAGE(battlerId, i) < MAX_STAT_STAGE)
|
if (STAT_STAGE(battlerId, i) < MAX_STAT_STAGE)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -673,3 +683,259 @@ static bool32 AreMainStatsMaxed(u8 battlerId)
|
|||||||
return TRUE;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -2385,6 +2385,8 @@ static const struct LevelUpMove sStarmieLevelUpLearnset[] = {
|
|||||||
LEVEL_UP_MOVE( 1, MOVE_RECOVER),
|
LEVEL_UP_MOVE( 1, MOVE_RECOVER),
|
||||||
LEVEL_UP_MOVE( 1, MOVE_SWIFT),
|
LEVEL_UP_MOVE( 1, MOVE_SWIFT),
|
||||||
LEVEL_UP_MOVE(40, MOVE_CONFUSE_RAY),
|
LEVEL_UP_MOVE(40, MOVE_CONFUSE_RAY),
|
||||||
|
LEVEL_UP_MOVE(40, MOVE_PSYCHIC),
|
||||||
|
LEVEL_UP_MOVE(50, MOVE_WATER_PULSE),
|
||||||
LEVEL_UP_END
|
LEVEL_UP_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -429,16 +429,18 @@ static const struct TrainerMonNoItemCustomMoves sParty_Felix[] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct TrainerMonNoItemDefaultMoves sParty_Violet[] = {
|
static const struct TrainerMonItemDefaultMoves sParty_Violet[] = {
|
||||||
{
|
{
|
||||||
.iv = 0,
|
.iv = 0,
|
||||||
.lvl = 26,
|
.lvl = 45,
|
||||||
.species = SPECIES_ROSELIA,
|
.species = SPECIES_ROSELIA,
|
||||||
|
.heldItem = ITEM_GRASSIUM_Z,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.iv = 0,
|
.iv = 0,
|
||||||
.lvl = 26,
|
.lvl = 40,
|
||||||
.species = SPECIES_GLOOM,
|
.species = SPECIES_GLOOM,
|
||||||
|
.heldItem = 0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ const struct Trainer gTrainers[] = {
|
|||||||
|
|
||||||
[TRAINER_VIOLET] =
|
[TRAINER_VIOLET] =
|
||||||
{
|
{
|
||||||
.partyFlags = 0,
|
.partyFlags = F_TRAINER_PARTY_HELD_ITEM,
|
||||||
.trainerClass = TRAINER_CLASS_AROMA_LADY,
|
.trainerClass = TRAINER_CLASS_AROMA_LADY,
|
||||||
.encounterMusic_gender = F_TRAINER_FEMALE | TRAINER_ENCOUNTER_MUSIC_FEMALE,
|
.encounterMusic_gender = F_TRAINER_FEMALE | TRAINER_ENCOUNTER_MUSIC_FEMALE,
|
||||||
.trainerPic = TRAINER_PIC_AROMA_LADY,
|
.trainerPic = TRAINER_PIC_AROMA_LADY,
|
||||||
@ -556,7 +556,7 @@ const struct Trainer gTrainers[] = {
|
|||||||
.doubleBattle = FALSE,
|
.doubleBattle = FALSE,
|
||||||
.aiFlags = AI_SCRIPT_CHECK_BAD_MOVE,
|
.aiFlags = AI_SCRIPT_CHECK_BAD_MOVE,
|
||||||
.partySize = ARRAY_COUNT(sParty_Violet),
|
.partySize = ARRAY_COUNT(sParty_Violet),
|
||||||
.party = {.NoItemDefaultMoves = sParty_Violet},
|
.party = {.ItemDefaultMoves = sParty_Violet},
|
||||||
},
|
},
|
||||||
|
|
||||||
[TRAINER_ROSE_2] =
|
[TRAINER_ROSE_2] =
|
||||||
|
Loading…
Reference in New Issue
Block a user