mirror of
https://github.com/pret/pokefirered.git
synced 2026-10-01 05:20:00 -05:00
updated AI
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -317,29 +317,75 @@ struct WishFutureKnock
|
||||
|
||||
extern struct WishFutureKnock gWishFutureKnock;
|
||||
|
||||
struct AI_SavedBattleMon
|
||||
{
|
||||
u16 ability;
|
||||
u16 moves[MAX_MON_MOVES];
|
||||
u16 heldItem;
|
||||
u16 species;
|
||||
u8 types[3];
|
||||
};
|
||||
|
||||
struct AiPartyMon
|
||||
{
|
||||
u16 species;
|
||||
u16 item;
|
||||
u16 heldEffect;
|
||||
u16 ability;
|
||||
u16 gender;
|
||||
u16 level;
|
||||
u16 moves[MAX_MON_MOVES];
|
||||
u32 status;
|
||||
bool8 isFainted;
|
||||
bool8 wasSentInBattle;
|
||||
u8 switchInCount; // Counts how many times this Pokemon has been sent out or switched into in a battle.
|
||||
};
|
||||
|
||||
struct AIPartyData // Opposing battlers - party mons.
|
||||
{
|
||||
struct AiPartyMon mons[NUM_BATTLE_SIDES][PARTY_SIZE]; // 2 parties(player, opponent). Used to save information on opposing party.
|
||||
u8 count[NUM_BATTLE_SIDES];
|
||||
};
|
||||
|
||||
struct SwitchinCandidate
|
||||
{
|
||||
struct BattlePokemon battleMon;
|
||||
bool8 hypotheticalStatus;
|
||||
};
|
||||
|
||||
// Ai Data used when deciding which move to use, computed only once before each turn's start.
|
||||
struct AiLogicData
|
||||
{
|
||||
u16 abilities[MAX_BATTLERS_COUNT];
|
||||
u16 items[MAX_BATTLERS_COUNT];
|
||||
u16 holdEffects[MAX_BATTLERS_COUNT];
|
||||
u8 holdEffectParams[MAX_BATTLERS_COUNT];
|
||||
u16 predictedMoves[MAX_BATTLERS_COUNT];
|
||||
u8 hpPercents[MAX_BATTLERS_COUNT];
|
||||
u16 partnerMove;
|
||||
u16 speedStats[MAX_BATTLERS_COUNT]; // Speed stats for all battles, calculated only once, same way as damages
|
||||
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||
u8 moveAccuracy[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||
u8 moveLimitations[MAX_BATTLERS_COUNT];
|
||||
bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler.
|
||||
u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch.
|
||||
bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once.
|
||||
u8 mostSuitableMonId[MAX_BATTLERS_COUNT]; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
|
||||
struct SwitchinCandidate switchinCandidate; // Struct used for deciding which mon to switch to in battle_ai_switch_items.c
|
||||
};
|
||||
|
||||
struct AI_ThinkingStruct
|
||||
{
|
||||
u8 aiState;
|
||||
u8 movesetIndex;
|
||||
u16 moveConsidered;
|
||||
s8 score[4];
|
||||
s32 score[MAX_MON_MOVES];
|
||||
u32 funcResult;
|
||||
u32 aiFlags;
|
||||
u32 aiFlags[MAX_BATTLERS_COUNT];
|
||||
u8 aiAction;
|
||||
u8 aiLogicId;
|
||||
u8 filler12[6];
|
||||
u8 simulatedRNG[4];
|
||||
};
|
||||
|
||||
extern u8 gBattlerTarget;
|
||||
extern u8 gAbsentBattlerFlags;
|
||||
|
||||
extern struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT];
|
||||
|
||||
struct UsedMoves
|
||||
{
|
||||
u16 moves[MAX_BATTLERS_COUNT];
|
||||
u16 unknown[MAX_BATTLERS_COUNT];
|
||||
struct AI_SavedBattleMon saved[MAX_BATTLERS_COUNT];
|
||||
};
|
||||
|
||||
#define AI_MOVE_HISTORY_COUNT 3
|
||||
@@ -392,6 +438,11 @@ struct BattleResources
|
||||
|
||||
extern struct BattleResources *gBattleResources;
|
||||
|
||||
#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai))
|
||||
#define AI_DATA ((struct AiLogicData *)(gBattleResources->aiData))
|
||||
#define AI_PARTY ((struct AIPartyData *)(gBattleResources->aiParty))
|
||||
#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory))
|
||||
|
||||
struct BattleResults
|
||||
{
|
||||
u8 playerFaintCounter; // 0x0
|
||||
@@ -659,6 +710,8 @@ struct BattleStruct
|
||||
s32 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier
|
||||
u8 aiMoveOrAction[MAX_BATTLERS_COUNT];
|
||||
u8 aiChosenTarget[MAX_BATTLERS_COUNT];
|
||||
u32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
|
||||
u32 aiDelayFrames; // Number of frames it took to choose an action.
|
||||
// pokeemerald unknown use
|
||||
u8 field_93; // related to choosing pokemon? probably related to recording
|
||||
};
|
||||
@@ -942,6 +995,7 @@ extern u8 *gBattleAnimBgTilemapBuffer;
|
||||
extern void (*gBattleMainFunc)(void);
|
||||
extern u8 gMoveSelectionCursor[MAX_BATTLERS_COUNT];
|
||||
extern u8 gBattlerAttacker;
|
||||
extern u8 gBattlerTarget;
|
||||
extern u8 gEffectBattler;
|
||||
extern u8 gMultiHitCounter;
|
||||
extern struct BattleScripting gBattleScripting;
|
||||
@@ -999,6 +1053,8 @@ extern u8 gLastUsedBall;
|
||||
extern u16 gLastThrownBall;
|
||||
extern u16 gBallToDisplay;
|
||||
extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT];
|
||||
extern struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT];
|
||||
extern u8 gAbsentBattlerFlags;
|
||||
|
||||
static inline u32 GetBattlerPosition(u32 battler)
|
||||
{
|
||||
|
||||
96
include/battle_ai_main.h
Normal file
96
include/battle_ai_main.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef GUARD_BATTLE_AI_MAIN_H
|
||||
#define GUARD_BATTLE_AI_MAIN_H
|
||||
|
||||
#define UNKNOWN_NO_OF_HITS UINT32_MAX
|
||||
|
||||
// return vals for BattleAI_ChooseMoveOrAction
|
||||
// 0 - 3 are move idx
|
||||
#define AI_CHOICE_FLEE 4
|
||||
#define AI_CHOICE_WATCH 5
|
||||
#define AI_CHOICE_SWITCH 7
|
||||
|
||||
// for AI_WhoStrikesFirst
|
||||
#define AI_IS_FASTER 1
|
||||
#define AI_IS_SLOWER -1
|
||||
|
||||
// for stat increasing / decreasing scores
|
||||
#define STAT_CHANGE_ATK 0
|
||||
#define STAT_CHANGE_DEF 1
|
||||
#define STAT_CHANGE_SPEED 2
|
||||
#define STAT_CHANGE_SPATK 3
|
||||
#define STAT_CHANGE_SPDEF 4
|
||||
|
||||
#define STAT_CHANGE_ATK_2 5
|
||||
#define STAT_CHANGE_DEF_2 6
|
||||
#define STAT_CHANGE_SPEED_2 7
|
||||
#define STAT_CHANGE_SPATK_2 8
|
||||
#define STAT_CHANGE_SPDEF_2 9
|
||||
|
||||
#define STAT_CHANGE_ACC 10
|
||||
#define STAT_CHANGE_EVASION 11
|
||||
|
||||
#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect
|
||||
#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target
|
||||
|
||||
// Temporary scores that are added together to determine a final score at the at of AI_CalcMoveScore
|
||||
#define WEAK_EFFECT 1
|
||||
#define DECENT_EFFECT 2
|
||||
#define GOOD_EFFECT 4
|
||||
#define BEST_EFFECT 6
|
||||
|
||||
// AI_CalcMoveScore final score
|
||||
#define NOT_GOOD_ENOUGH 0 // Not worth using over a damaging move
|
||||
#define GOOD_MOVE_EFFECTS 2 // Worth using over a damaging move
|
||||
#define PREFERRED_MOVE_EFFECTS 3 // Worth using over a damagin move and is better then DECENT_EFFECT
|
||||
#define BEST_MOVE_EFFECTS 4 // Best possible move effects. E.g. stat boosting moves that boost multiply moves
|
||||
|
||||
// AI_TryToFaint
|
||||
#define FAST_KILL 6 // AI is faster and faints target
|
||||
#define SLOW_KILL 4 // AI is slower and faints target
|
||||
#define LAST_CHANCE 2 // AI faints to target. It should try and do damage with a priority move
|
||||
|
||||
|
||||
// Logs for debugging AI tests.
|
||||
#define SET_SCORE(battler, movesetIndex, val) \
|
||||
do \
|
||||
{ \
|
||||
AI_THINKING_STRUCT->score[movesetIndex] = val; \
|
||||
} while (0) \
|
||||
|
||||
#define ADJUST_SCORE(val) \
|
||||
do \
|
||||
{ \
|
||||
score += val; \
|
||||
} while (0) \
|
||||
|
||||
#define ADJUST_SCORE_PTR(val) \
|
||||
do \
|
||||
{ \
|
||||
(*score) += val; \
|
||||
} while (0) \
|
||||
|
||||
#define RETURN_SCORE_PLUS(val) \
|
||||
{ \
|
||||
ADJUST_SCORE(val); \
|
||||
return score; \
|
||||
}
|
||||
|
||||
#define RETURN_SCORE_MINUS(val) \
|
||||
{ \
|
||||
ADJUST_SCORE(-val); \
|
||||
return score; \
|
||||
}
|
||||
|
||||
u32 ComputeBattleAiScores(u32 battler);
|
||||
void BattleAI_SetupItems(void);
|
||||
void BattleAI_SetupFlags(void);
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler);
|
||||
u32 BattleAI_ChooseMoveOrAction(void);
|
||||
void Ai_InitPartyStruct(void);
|
||||
void Ai_UpdateSwitchInData(u32 battler);
|
||||
void Ai_UpdateFaintData(u32 battler);
|
||||
void SetAiLogicDataForTurn(struct AiLogicData *aiData);
|
||||
|
||||
extern u8 sBattler_AI;
|
||||
|
||||
#endif // GUARD_BATTLE_AI_MAIN_H
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef GUARD_BATTLE_AI_SCRIPT_COMMANDS_H
|
||||
#define GUARD_BATTLE_AI_SCRIPT_COMMANDS_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
// return values for BattleAI_ChooseMoveOrAction
|
||||
// 0 - 3 are move idx
|
||||
#define AI_CHOICE_FLEE 4
|
||||
#define AI_CHOICE_WATCH 5
|
||||
#define AI_CHOICE_SWITCH 7
|
||||
|
||||
void BattleAI_HandleItemUseBeforeAISetup(void);
|
||||
void BattleAI_SetupAIData(u32 battler);
|
||||
u8 BattleAI_ChooseMoveOrAction(void);
|
||||
void ClearBankMoveHistory(u8 bank);
|
||||
void RecordAbilityBattle(u32 bank, u32 abilityId);
|
||||
void ClearBankAbilityHistory(u8 bank);
|
||||
void RecordItemEffectBattle(u32 bank, u32 itemEffect);
|
||||
void ClearBankItemEffectHistory(u8 bank);
|
||||
u8 BattleAI_ChooseMoveOrAction(void);
|
||||
|
||||
#endif // GUARD_BATTLE_AI_SCRIPT_COMMANDS_H
|
||||
@@ -1,27 +1,9 @@
|
||||
#ifndef GUARD_BATTLE_AI_SWITCH_ITEMS_H
|
||||
#define GUARD_BATTLE_AI_SWITCH_ITEMS_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
enum {
|
||||
AI_ITEM_FULL_RESTORE = 1,
|
||||
AI_ITEM_HEAL_HP,
|
||||
AI_ITEM_CURE_CONDITION,
|
||||
AI_ITEM_X_STAT,
|
||||
AI_ITEM_GUARD_SPECS,
|
||||
AI_ITEM_NOT_RECOGNIZABLE
|
||||
};
|
||||
|
||||
enum {
|
||||
AI_HEAL_CONFUSION,
|
||||
AI_HEAL_PARALYSIS,
|
||||
AI_HEAL_FREEZE,
|
||||
AI_HEAL_BURN,
|
||||
AI_HEAL_POISON,
|
||||
AI_HEAL_SLEEP,
|
||||
};
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
void AI_TrySwitchOrUseItem(u32 battler);
|
||||
u8 GetMostSuitableMonToSwitchInto(u32 battler);
|
||||
u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd);
|
||||
bool32 ShouldSwitch(u32 battler, bool32 emitResult);
|
||||
|
||||
#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H
|
||||
|
||||
196
include/battle_ai_util.h
Normal file
196
include/battle_ai_util.h
Normal file
@@ -0,0 +1,196 @@
|
||||
#ifndef GUARD_BATTLE_AI_UTIL_H
|
||||
#define GUARD_BATTLE_AI_UTIL_H
|
||||
|
||||
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
||||
|
||||
#define AI_STRIKES_FIRST(battlerAi, battlerDef, move)((AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER))
|
||||
|
||||
bool32 AI_RandLessThan(u32 val);
|
||||
bool32 IsAiVsAiBattle(void);
|
||||
bool32 BattlerHasAi(u32 battlerId);
|
||||
bool32 IsAiBattlerAware(u32 battlerId);
|
||||
void ClearBattlerMoveHistory(u32 battlerId);
|
||||
void RecordLastUsedMoveBy(u32 battlerId, u32 move);
|
||||
void RecordAllMoves(u32 battler);
|
||||
void RecordKnownMove(u32 battlerId, u32 move);
|
||||
void RecordAbilityBattle(u32 battlerId, u32 abilityId);
|
||||
void ClearBattlerAbilityHistory(u32 battlerId);
|
||||
void RecordItemEffectBattle(u32 battlerId, u32 itemEffect);
|
||||
void ClearBattlerItemEffectHistory(u32 battlerId);
|
||||
void SaveBattlerData(u32 battlerId);
|
||||
void SetBattlerData(u32 battlerId);
|
||||
void RestoreBattlerData(u32 battlerId);
|
||||
u32 GetAIChosenMove(u32 battlerId);
|
||||
u32 GetTotalBaseStat(u32 species);
|
||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||
bool32 AtMaxHp(u32 battler);
|
||||
u32 GetHealthPercentage(u32 battler);
|
||||
bool32 IsBattlerTrapped(u32 battler, bool32 switching);
|
||||
s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
|
||||
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
|
||||
u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk);
|
||||
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits);
|
||||
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
|
||||
s32 AI_DecideKnownAbilityForTurn(u32 battlerId);
|
||||
u32 AI_DecideHoldEffectForTurn(u32 battlerId);
|
||||
bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move);
|
||||
u32 AI_GetWeather(struct AiLogicData *aiData);
|
||||
bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits);
|
||||
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits);
|
||||
bool32 AI_IsTerrainAffected(u32 battlerId, u32 flags);
|
||||
bool32 AI_IsBattlerGrounded(u32 battlerId);
|
||||
bool32 HasDamagingMove(u32 battlerId);
|
||||
bool32 HasDamagingMoveOfType(u32 battlerId, u32 type);
|
||||
u32 GetBattlerSecondaryDamage(u32 battlerId);
|
||||
bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability);
|
||||
bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability);
|
||||
bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move);
|
||||
bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex);
|
||||
u32 GetBattlerSideSpeedAverage(u32 battler);
|
||||
bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage);
|
||||
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent);
|
||||
bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect);
|
||||
bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex);
|
||||
bool32 IsRecycleEncouragedItem(u32 item);
|
||||
bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item);
|
||||
bool32 IsStatBoostingBerry(u32 item);
|
||||
bool32 CanKnockOffItem(u32 battler, u32 item);
|
||||
bool32 IsAbilityOfRating(u32 ability, s8 rating);
|
||||
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
|
||||
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move);
|
||||
u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move);
|
||||
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove);
|
||||
|
||||
// stat stage checks
|
||||
bool32 AnyStatIsRaised(u32 battlerId);
|
||||
bool32 ShouldLowerStat(u32 battler, u32 battlerAbility, u32 stat);
|
||||
bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat);
|
||||
bool32 AreBattlersStatsMaxed(u32 battler);
|
||||
bool32 BattlerHasAnyStatRaised(u32 battlerId);
|
||||
u32 CountPositiveStatStages(u32 battlerId);
|
||||
u32 CountNegativeStatStages(u32 battlerId);
|
||||
bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
|
||||
// move checks
|
||||
bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect);
|
||||
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category);
|
||||
s32 AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
|
||||
s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower);
|
||||
s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather);
|
||||
bool32 AI_IsDamagedByRecoil(u32 battler);
|
||||
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
||||
u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef);
|
||||
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
|
||||
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
|
||||
uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
|
||||
u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
|
||||
u16 *GetMovesArray(u32 battler);
|
||||
bool32 IsConfusionMoveEffect(u32 moveEffect);
|
||||
bool32 HasMove(u32 battlerId, u32 move);
|
||||
bool32 HasOnlyMovesWithCategory(u32 battlerId, u32 category, bool32 onlyOffensive);
|
||||
bool32 HasMoveWithCategory(u32 battler, u32 category);
|
||||
bool32 HasMoveWithType(u32 battler, u32 type);
|
||||
bool32 HasMoveEffect(u32 battlerId, u32 moveEffect);
|
||||
bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument);
|
||||
bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect);
|
||||
bool32 HasMoveWithCriticalHitChance(u32 battlerId);
|
||||
bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception);
|
||||
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
|
||||
bool32 IsAromaVeilProtectedMove(u32 move);
|
||||
bool32 IsNonVolatileStatusMoveEffect(u32 moveEffect);
|
||||
bool32 IsStatLoweringMoveEffect(u32 moveEffect);
|
||||
bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility);
|
||||
bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 IsHazardMoveEffect(u32 moveEffect);
|
||||
bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move);
|
||||
void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score);
|
||||
bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect);
|
||||
bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect);
|
||||
bool32 ShouldSetSnow(u32 battler, u32 ability, u32 holdEffect);
|
||||
bool32 ShouldSetRain(u32 battlerAtk, u32 ability, u32 holdEffect);
|
||||
bool32 ShouldSetSun(u32 battlerAtk, u32 atkAbility, u32 holdEffect);
|
||||
bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 IsHealingMove(u32 move);
|
||||
bool32 HasHealingEffect(u32 battler);
|
||||
bool32 IsTrappingMove(u32 move);
|
||||
bool32 HasTrappingMoveEffect(u32 battler);
|
||||
bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 HasThawingMove(u32 battler);
|
||||
bool32 IsStatRaisingEffect(u32 effect);
|
||||
bool32 IsStatLoweringEffect(u32 effect);
|
||||
bool32 IsAttackBoostMoveEffect(u32 effect);
|
||||
bool32 IsUngroundingEffect(u32 effect);
|
||||
bool32 IsSemiInvulnerable(u32 battlerDef, u32 move);
|
||||
bool32 HasSubstituteIgnoringMove(u32 battler);
|
||||
bool32 HasHighCritRatioMove(u32 battler);
|
||||
bool32 HasMagicCoatAffectedMove(u32 battler);
|
||||
bool32 HasSnatchAffectedMove(u32 battler);
|
||||
|
||||
// status checks
|
||||
bool32 AI_CanBeBurned(u32 battler, u32 ability);
|
||||
bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
|
||||
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability);
|
||||
bool32 AI_CanSleep(u32 battler, u32 ability);
|
||||
bool32 IsBattlerIncapacitated(u32 battler, u32 ability);
|
||||
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
|
||||
bool32 ShouldPoisonSelf(u32 battler, u32 ability);
|
||||
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
|
||||
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
|
||||
bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 ShouldBurnSelf(u32 battler, u32 ability);
|
||||
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility);
|
||||
bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof);
|
||||
u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move);
|
||||
bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 IsWakeupTurn(u32 battler);
|
||||
bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId);
|
||||
|
||||
// partner logic
|
||||
#define IS_TARGETING_PARTNER(battlerAtk, battlerDef)((battlerAtk) == (battlerDef ^ BIT_FLANK))
|
||||
u32 GetAllyChosenMove(u32 battlerId);
|
||||
bool32 IsValidDoubleBattle(u32 battlerAtk);
|
||||
bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove);
|
||||
bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove);
|
||||
bool32 IsMoveEffectWeather(u32 move);
|
||||
bool32 PartnerMoveEffectIsTerrain(u32 battlerAtkPartner, u32 partnerMove);
|
||||
bool32 PartnerMoveIs(u32 battlerAtkPartner, u32 partnerMove, u32 moveCheck);
|
||||
bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove);
|
||||
bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
|
||||
// party logic
|
||||
struct BattlePokemon *AllocSaveBattleMons(void);
|
||||
void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons);
|
||||
s32 CountUsablePartyMons(u32 battlerId);
|
||||
bool32 IsPartyFullyHealedExceptBattler(u32 battler);
|
||||
bool32 PartyHasMoveCategory(u32 battlerId, u32 category);
|
||||
bool32 SideHasMoveCategory(u32 battlerId, u32 category);
|
||||
|
||||
// score increases
|
||||
void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score);
|
||||
void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
|
||||
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker);
|
||||
s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle);
|
||||
s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle);
|
||||
bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
|
||||
void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
|
||||
#endif //GUARD_BATTLE_AI_UTIL_H
|
||||
@@ -53,6 +53,11 @@ bool32 CanBattlerSwitch(u32 battlerId);
|
||||
u8 GetFirstFaintedPartyIndex(u8 battlerId);
|
||||
u16 GetNaturePowerMove(void);
|
||||
bool32 CanCamouflage(u8 battlerId);
|
||||
bool32 IsTelekinesisBannedSpecies(u16 species);
|
||||
bool32 CanUseLastResort(u8 battlerId);
|
||||
bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType);
|
||||
s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk);
|
||||
s32 GetCritHitChance(s32 critChanceIndex);
|
||||
|
||||
extern void (* const gBattleScriptingCommandsTable[])(void);
|
||||
extern const struct StatFractions gAccuracyStageRatios[];
|
||||
|
||||
@@ -148,7 +148,7 @@ void BattleScriptPushCursorAndCallback(const u8 *BS_ptr);
|
||||
u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn);
|
||||
void ClearVariousBattlerFlags(u32 battler);
|
||||
void HandleAction_RunBattleScript(void);
|
||||
u8 GetMoveTarget(u16 move, u8 setTarget);
|
||||
u32 GetMoveTarget(u16 move, u8 setTarget);
|
||||
u8 IsMonDisobedient(void);
|
||||
void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1);
|
||||
// new
|
||||
@@ -178,6 +178,7 @@ bool32 IsBattlerProtected(u32 battler, u32 move);
|
||||
bool32 IsMoveMakingContact(u32 move, u32 battlerAtk);
|
||||
bool32 IsHealBlockPreventingMove(u32 battler, u32 move);
|
||||
s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
|
||||
bool32 MoveHasAdditionalEffectWithChance(u32 move, u32 moveEffect, u32 chance);
|
||||
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
|
||||
u32 GetMoveSlot(u16 *moves, u32 move);
|
||||
u32 GetBattlerWeight(u32 battler);
|
||||
@@ -191,6 +192,7 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2);
|
||||
bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2);
|
||||
u32 GetBattlerHoldEffectParam(u32 battler);
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities);
|
||||
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
|
||||
bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect);
|
||||
uq4_12_t GetTypeModifier(u32 atkType, u32 defType);
|
||||
u8 GetBattlerType(u32 battler, u8 typeIndex);
|
||||
@@ -245,17 +247,24 @@ void TryClearRageAndFuryCutter(void);
|
||||
bool32 CanMegaEvolve(u32 battler);
|
||||
bool32 CanUltraBurst(u32 battler);
|
||||
bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move);
|
||||
u32 SetRandomTarget(u32 battler);
|
||||
bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect);
|
||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||
s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef);
|
||||
|
||||
|
||||
// battle_ai_util.h
|
||||
bool32 IsHealingMove(u32 move);
|
||||
void RecordKnownMove(u32 battlerId, u32 move);
|
||||
s32 CountUsablePartyMons(u32 battlerId);
|
||||
bool32 IsAiVsAiBattle(void);
|
||||
void RecordLastUsedMoveBy(u32 battlerId, u32 move);
|
||||
bool32 BattlerHasAi(u32 battlerId);
|
||||
void ClearBattlerItemEffectHistory(u32 battlerId);
|
||||
bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect);
|
||||
void RecordAllMoves(u32 battler);
|
||||
// bool32 IsHealingMove(u32 move);
|
||||
// void RecordKnownMove(u32 battlerId, u32 move);
|
||||
// s32 CountUsablePartyMons(u32 battlerId);
|
||||
// bool32 IsAiVsAiBattle(void);
|
||||
// void RecordLastUsedMoveBy(u32 battlerId, u32 move);
|
||||
// bool32 BattlerHasAi(u32 battlerId);
|
||||
// void ClearBattlerItemEffectHistory(u32 battlerId);
|
||||
// bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect);
|
||||
// void RecordAllMoves(u32 battler);
|
||||
|
||||
// end battle_ai_util.h
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
#define IS_BATTLE_TYPE_GHOST_WITH_SCOPE(flags) ((flags) & BATTLE_TYPE_GHOST && (flags) & BATTLE_TYPE_GHOST_UNVEILED)
|
||||
#define WILD_DOUBLE_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))))
|
||||
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gTrainerBattleOpponent_B == 0xFFFF))
|
||||
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER)
|
||||
|
||||
#define RIVAL_BATTLE_HEAL_AFTER 1
|
||||
#define RIVAL_BATTLE_TUTORIAL 3
|
||||
|
||||
@@ -15,38 +15,45 @@
|
||||
#define AI_TYPE_MOVE 4
|
||||
|
||||
// type effectiveness
|
||||
#define AI_EFFECTIVENESS_x4 160
|
||||
#define AI_EFFECTIVENESS_x2 80
|
||||
#define AI_EFFECTIVENESS_x1 40
|
||||
#define AI_EFFECTIVENESS_x0_5 20
|
||||
#define AI_EFFECTIVENESS_x0_25 10
|
||||
#define AI_EFFECTIVENESS_x8 7
|
||||
#define AI_EFFECTIVENESS_x4 6
|
||||
#define AI_EFFECTIVENESS_x2 5
|
||||
#define AI_EFFECTIVENESS_x1 4
|
||||
#define AI_EFFECTIVENESS_x0_5 3
|
||||
#define AI_EFFECTIVENESS_x0_25 2
|
||||
#define AI_EFFECTIVENESS_x0_125 1
|
||||
#define AI_EFFECTIVENESS_x0 0
|
||||
|
||||
// ai weather
|
||||
#define AI_WEATHER_SUN 0
|
||||
#define AI_WEATHER_RAIN 1
|
||||
#define AI_WEATHER_SANDSTORM 2
|
||||
#define AI_WEATHER_HAIL 3
|
||||
// AI Flags. Most run specific functions to update score, new flags are used for internal logic in other scripts
|
||||
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0)
|
||||
#define AI_FLAG_TRY_TO_FAINT (1 << 1)
|
||||
#define AI_FLAG_CHECK_VIABILITY (1 << 2)
|
||||
#define AI_FLAG_SETUP_FIRST_TURN (1 << 3)
|
||||
#define AI_FLAG_RISKY (1 << 4)
|
||||
#define AI_FLAG_PREFER_STRONGEST_MOVE (1 << 5)
|
||||
#define AI_FLAG_PREFER_BATON_PASS (1 << 6)
|
||||
#define AI_FLAG_DOUBLE_BATTLE (1 << 7) // removed, split between AI_FLAG_CHECK_BAD_MOVE & AI_FLAG_CHECK_GOOD_MOVE
|
||||
#define AI_FLAG_HP_AWARE (1 << 8)
|
||||
#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target
|
||||
// New, Trainer Handicap Flags
|
||||
#define AI_FLAG_NEGATE_UNAWARE (1 << 10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc
|
||||
#define AI_FLAG_WILL_SUICIDE (1 << 11) // AI will use explosion / self destruct / final gambit / etc
|
||||
// New, Trainer Strategy Flags
|
||||
#define AI_FLAG_HELP_PARTNER (1 << 12) // AI can try to help partner. If not set, will tend not to target partner
|
||||
#define AI_FLAG_PREFER_STATUS_MOVES (1 << 13) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves
|
||||
#define AI_FLAG_STALL (1 << 14) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished
|
||||
#define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks. Automatically includes AI_FLAG_SMART_MON_CHOICES.
|
||||
#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
|
||||
#define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items
|
||||
#define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
|
||||
|
||||
// get_how_powerful_move_is
|
||||
#define MOVE_POWER_DISCOURAGED 0
|
||||
#define MOVE_NOT_MOST_POWERFUL 1
|
||||
#define MOVE_MOST_POWERFUL 2
|
||||
#define AI_FLAG_COUNT 19
|
||||
|
||||
// script's table id to bit
|
||||
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0)
|
||||
#define AI_FLAG_CHECK_VIABILITY (1 << 1)
|
||||
#define AI_FLAG_TRY_TO_FAINT (1 << 2)
|
||||
#define AI_SCRIPT_SETUP_FIRST_TURN (1 << 3)
|
||||
#define AI_SCRIPT_RISKY (1 << 4)
|
||||
#define AI_SCRIPT_PREFER_STRONGEST_MOVE (1 << 5)
|
||||
#define AI_SCRIPT_PREFER_BATON_PASS (1 << 6)
|
||||
#define AI_SCRIPT_DOUBLE_BATTLE (1 << 7)
|
||||
#define AI_SCRIPT_HP_AWARE (1 << 8)
|
||||
#define AI_SCRIPT_UNKNOWN (1 << 9)
|
||||
// 10 - 28 are not used
|
||||
#define AI_SCRIPT_ROAMING (1 << 29)
|
||||
#define AI_SCRIPT_SAFARI (1 << 30)
|
||||
#define AI_SCRIPT_FIRST_BATTLE (1 << 31)
|
||||
// 'other' ai logic flags
|
||||
#define AI_FLAG_ROAMING (1 << 29)
|
||||
#define AI_FLAG_SAFARI (1 << 30)
|
||||
#define AI_FLAG_FIRST_BATTLE (1 << 31)
|
||||
|
||||
#define AI_SCORE_DEFAULT 100 // Default score for all AI moves.
|
||||
|
||||
#endif // GUARD_CONSTANTS_BATTLE_AI_H
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
#define ITEM_NAME_LENGTH 14
|
||||
#define ITEM_NAME_PLURAL_LENGTH ITEM_NAME_LENGTH + 2 // 2 is used for the instance where a word's suffix becomes y->ies
|
||||
#define POKEMON_NAME_LENGTH 10
|
||||
#define POKEMON_NAME_BUFFER_SIZE max(20, POKEMON_NAME_LENGTH + 1) // Frequently used buffer size. Larger than necessary
|
||||
#define PLAYER_NAME_LENGTH 7
|
||||
#define MAIL_WORDS_COUNT 9
|
||||
#define EASY_CHAT_BATTLE_WORDS_COUNT 6
|
||||
|
||||
@@ -745,10 +745,11 @@ const struct FormChange *GetSpeciesFormChanges(u16 species);
|
||||
u8 CalculatePPWithBonus(u16 move, u8 ppBonuses, u8 moveIndex);
|
||||
void RemoveMonPPBonus(struct Pokemon *mon, u8 moveIndex);
|
||||
void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex);
|
||||
void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst);
|
||||
bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex);
|
||||
bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, bool8 usedByAI);
|
||||
u8 GetItemEffectParamOffset(u32 battler, u16 itemId, u8 effectByte, u8 effectBit);
|
||||
// const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId);
|
||||
u8 CanLearnTeachableMove(u16 species, u16 move);
|
||||
u8 GetNature(struct Pokemon *mon);
|
||||
u8 GetNatureFromPersonality(u32 personality);
|
||||
u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem, struct Pokemon *tradePartner);
|
||||
@@ -820,5 +821,6 @@ bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battleId);
|
||||
u16 SanitizeSpeciesId(u16 species);
|
||||
bool32 IsSpeciesEnabled(u16 species);
|
||||
u32 GetUnownSpeciesId(u32 personality);
|
||||
u8 CalculatePartyCount(struct Pokemon *party);
|
||||
|
||||
#endif // GUARD_POKEMON_H
|
||||
|
||||
@@ -199,7 +199,6 @@ SECTIONS {
|
||||
src/heal_location.o(.text);
|
||||
src/region_map.o(.text);
|
||||
src/image_processing_effects.o(.text);
|
||||
src/battle_ai_script_commands.o(.text);
|
||||
src/wallclock.o(.text);
|
||||
src/fldeff_rocksmash.o(.text);
|
||||
src/fldeff_dig.o(.text);
|
||||
@@ -288,6 +287,8 @@ SECTIONS {
|
||||
src/itemfinder.o(.text);
|
||||
src/buy_menu_helpers.o(.text);
|
||||
src/slot_machine.o(.text);
|
||||
src/battle_ai_main.o(.text);
|
||||
src/battle_ai_util.o(.text);
|
||||
src/roamer.o(.text);
|
||||
src/mystery_gift_menu.o(.text);
|
||||
src/ereader_screen.o(.text);
|
||||
@@ -329,7 +330,6 @@ SECTIONS {
|
||||
data/battle_scripts_1.o(script_data);
|
||||
data/field_effect_scripts.o(script_data);
|
||||
data/battle_scripts_2.o(script_data);
|
||||
data/battle_ai_scripts.o(script_data);
|
||||
data/mystery_event_script_cmd_table.o(script_data);
|
||||
} > ROM =0
|
||||
|
||||
@@ -429,6 +429,7 @@ SECTIONS {
|
||||
src/battle_controller_player.o(.rodata);
|
||||
src/battle_anim_smokescreen.o(.rodata);
|
||||
src/battle_controller_opponent.o(.rodata);
|
||||
src/battle_ai_switch_items.o(.rodata);
|
||||
src/battle_controller_link_opponent.o(.rodata);
|
||||
src/pokemon.o(.rodata);
|
||||
src/trig.o(.rodata);
|
||||
@@ -514,7 +515,6 @@ SECTIONS {
|
||||
src/heal_location.o(.rodata);
|
||||
src/region_map.o(.rodata);
|
||||
src/image_processing_effects.o(.rodata);
|
||||
src/battle_ai_script_commands.o(.rodata);
|
||||
src/wallclock.o(.rodata);
|
||||
src/fldeff_flash.o(.rodata);
|
||||
src/time_events.o(.rodata);
|
||||
@@ -588,6 +588,8 @@ SECTIONS {
|
||||
src/itemfinder.o(.rodata);
|
||||
src/buy_menu_helpers.o(.rodata);
|
||||
src/slot_machine.o(.rodata);
|
||||
src/battle_ai_main.o(.rodata);
|
||||
src/battle_ai_util.o(.rodata);
|
||||
src/roamer.o(.rodata);
|
||||
src/mystery_gift_menu.o(.rodata);
|
||||
src/ereader_screen.o(.rodata);
|
||||
|
||||
5192
src/battle_ai_main.c
Normal file
5192
src/battle_ai_main.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3724
src/battle_ai_util.c
Normal file
3724
src/battle_ai_util.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,21 @@
|
||||
#include "pokeball.h"
|
||||
#include "random.h"
|
||||
#include "battle.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_controllers.h"
|
||||
#include "battle_message.h"
|
||||
#include "battle_interface.h"
|
||||
#include "battle_tower.h"
|
||||
#include "battle_gfx_sfx_util.h"
|
||||
#include "battle_ai_script_commands.h"
|
||||
#include "battle_ai_switch_items.h"
|
||||
#include "party_menu.h"
|
||||
#include "trainer_tower.h"
|
||||
#include "constants/battle_ai.h"
|
||||
#include "constants/battle_anim.h"
|
||||
#include "constants/moves.h"
|
||||
#include "constants/party_menu.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/sound.h"
|
||||
|
||||
@@ -35,6 +39,7 @@ static void OpponentHandleIntroTrainerBallThrow(u32 battler);
|
||||
static void OpponentHandleDrawPartyStatusSummary(u32 battler);
|
||||
static void OpponentHandleBattleAnimation(u32 battler);
|
||||
static void OpponentHandleEndLinkBattle(u32 battler);
|
||||
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore);
|
||||
|
||||
static void OpponentBufferRunCommand(u32 battler);
|
||||
static void SwitchIn_HandleSoundAndEnd(u32 battler);
|
||||
@@ -356,50 +361,117 @@ static void OpponentHandleChooseMove(u32 battler)
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
|
||||
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER))
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
|| IsWildMonSmart())
|
||||
{
|
||||
// TODO: update with AI refactoring
|
||||
BattleAI_SetupAIData(battler);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
chosenMoveId = gBattleStruct->aiMoveOrAction[battler];
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[battler];
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_WATCH:
|
||||
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
|
||||
break;
|
||||
case AI_CHOICE_FLEE:
|
||||
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_RUN, 0);
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0);
|
||||
break;
|
||||
case AI_CHOICE_SWITCH:
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF);
|
||||
break;
|
||||
case 6:
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 15, gBattlerTarget);
|
||||
break;
|
||||
default:
|
||||
if (gMovesInfo[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = battler;
|
||||
if (gMovesInfo[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];
|
||||
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
|
||||
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
|
||||
const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
|
||||
bool32 shouldDynamax = FALSE;
|
||||
if (party != NULL)
|
||||
shouldDynamax = party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax;
|
||||
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = battler;
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH)
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||
}
|
||||
// TODO: Z-Moves
|
||||
// if (ShouldUseZMove(battler, gBattlerTarget, chosenMove))
|
||||
// QueueZMove(battler, chosenMove);
|
||||
// If opponent can Mega Evolve, do it.
|
||||
if (CanMegaEvolve(battler))
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
// If opponent can Ultra Burst, do it.
|
||||
else if (CanUltraBurst(battler))
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8));
|
||||
// If opponent can Dynamax and is allowed in the partydata, do it.
|
||||
else if (CanDynamax(battler) && shouldDynamax)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_DYNAMAX) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
}
|
||||
BtlController_EmitTwoReturnValues(battler, 1, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
break;
|
||||
}
|
||||
OpponentBufferExecCompleted(battler);
|
||||
}
|
||||
else
|
||||
else // Wild pokemon - use random move
|
||||
{
|
||||
u16 move;
|
||||
|
||||
u8 target;
|
||||
do
|
||||
{
|
||||
chosenMoveId = Random() & 3;
|
||||
move = moveInfo->moves[chosenMoveId];
|
||||
}
|
||||
while (move == MOVE_NONE);
|
||||
if (gMovesInfo[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
BtlController_EmitTwoReturnValues(battler, 1, 10, (chosenMoveId) | (battler << 8));
|
||||
} while (move == MOVE_NONE);
|
||||
|
||||
if (GetBattlerMoveTargetType(battler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (battler << 8));
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
BtlController_EmitTwoReturnValues(battler, 1, 10, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8));
|
||||
{
|
||||
do {
|
||||
target = GetBattlerAtPosition(Random() & 2);
|
||||
} while (!CanTargetBattler(battler, target, move));
|
||||
|
||||
// Don't bother to loop through table if the move can't attack ally
|
||||
if (B_WILD_NATURAL_ENEMIES == TRUE && !(gMovesInfo[move].target & MOVE_TARGET_BOTH))
|
||||
{
|
||||
u16 i, speciesAttacker, speciesTarget, isPartnerEnemy = FALSE;
|
||||
static const u16 naturalEnemies[][2] =
|
||||
{
|
||||
// Attacker Target
|
||||
{SPECIES_ZANGOOSE, SPECIES_SEVIPER},
|
||||
{SPECIES_SEVIPER, SPECIES_ZANGOOSE},
|
||||
{SPECIES_HEATMOR, SPECIES_DURANT},
|
||||
{SPECIES_DURANT, SPECIES_HEATMOR},
|
||||
{SPECIES_SABLEYE, SPECIES_CARBINK},
|
||||
{SPECIES_MAREANIE, SPECIES_CORSOLA},
|
||||
};
|
||||
speciesAttacker = gBattleMons[battler].species;
|
||||
speciesTarget = gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(battler))].species;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(naturalEnemies); i++)
|
||||
{
|
||||
if (speciesAttacker == naturalEnemies[i][0] && speciesTarget == naturalEnemies[i][1])
|
||||
{
|
||||
isPartnerEnemy = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPartnerEnemy && CanTargetBattler(battler, target, move))
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8));
|
||||
}
|
||||
}
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, 1, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
|
||||
|
||||
OpponentBufferExecCompleted(battler);
|
||||
}
|
||||
@@ -414,14 +486,20 @@ static void OpponentHandleChooseItem(u32 battler)
|
||||
static void OpponentHandleChoosePokemon(u32 battler)
|
||||
{
|
||||
s32 chosenMonId;
|
||||
s32 pokemonInBattle = 1;
|
||||
|
||||
if (*(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(battler) >> 1)) == PARTY_SIZE)
|
||||
// Choosing Revival Blessing target
|
||||
if ((gBattleResources->bufferA[battler][1] & 0xF) == PARTY_ACTION_CHOOSE_FAINTED_MON)
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler);
|
||||
|
||||
chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler);
|
||||
}
|
||||
// Switching out
|
||||
else if (*(gBattleStruct->AI_monToSwitchIntoId + battler) == PARTY_SIZE)
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE);
|
||||
if (chosenMonId == PARTY_SIZE)
|
||||
{
|
||||
s32 battler1, battler2;
|
||||
s32 battler1, battler2, firstId, lastId;
|
||||
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
{
|
||||
@@ -431,24 +509,52 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
||||
{
|
||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
pokemonInBattle = 2;
|
||||
}
|
||||
for (chosenMonId = 0; chosenMonId < PARTY_SIZE; ++chosenMonId)
|
||||
if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler2])
|
||||
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
|
||||
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
||||
{
|
||||
if (IsValidForBattle(&gEnemyParty[chosenMonId])
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler2]
|
||||
&& (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON)
|
||||
|| chosenMonId != CalculateEnemyPartyCount() - 1
|
||||
|| CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
||||
}
|
||||
else
|
||||
{
|
||||
chosenMonId = *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(battler) >> 1));
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(battler) >> 1)) = PARTY_SIZE;
|
||||
chosenMonId = *(gBattleStruct->AI_monToSwitchIntoId + battler);
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + battler) = PARTY_SIZE;
|
||||
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
||||
}
|
||||
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
||||
BtlController_EmitChosenMonReturnValue(battler, 1, chosenMonId, NULL);
|
||||
BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, chosenMonId, NULL);
|
||||
OpponentBufferExecCompleted(battler);
|
||||
}
|
||||
|
||||
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore)
|
||||
{
|
||||
u16 i, count;
|
||||
|
||||
for (i = 0, count = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (i != slotToIgnore
|
||||
&& IsValidForBattle(&gEnemyParty[i]))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void OpponentHandleHealthBarUpdate(u32 battler)
|
||||
{
|
||||
BtlController_HandleHealthBarUpdate(battler, FALSE);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "battle_ai_script_commands.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_controllers.h"
|
||||
#include "battle_interface.h"
|
||||
@@ -70,7 +71,8 @@ void SetUpBattleVars(void)
|
||||
gBattleControllerExecFlags = 0;
|
||||
ClearBattleAnimationVars();
|
||||
ClearBattleMonForms();
|
||||
BattleAI_HandleItemUseBeforeAISetup();
|
||||
BattleAI_SetupItems();
|
||||
BattleAI_SetupFlags();
|
||||
}
|
||||
|
||||
void InitBattleControllers(void)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "global.h"
|
||||
#include "gflib.h"
|
||||
#include "battle.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_ai_script_commands.h"
|
||||
#include "battle_controllers.h"
|
||||
#include "battle_interface.h"
|
||||
#include "battle_main.h"
|
||||
|
||||
@@ -2149,15 +2149,15 @@ const u16 gSafariReactionStringIds[NUM_SAFARI_REACTIONS] =
|
||||
[B_MSG_MON_EATING] = STRINGID_PKMNEATING
|
||||
};
|
||||
|
||||
const u16 gTrainerItemCuredStatusStringIds[] =
|
||||
{
|
||||
[AI_HEAL_CONFUSION] = STRINGID_PKMNSITEMSNAPPEDOUT,
|
||||
[AI_HEAL_PARALYSIS] = STRINGID_PKMNSITEMCUREDPARALYSIS,
|
||||
[AI_HEAL_FREEZE] = STRINGID_PKMNSITEMDEFROSTEDIT,
|
||||
[AI_HEAL_BURN] = STRINGID_PKMNSITEMHEALEDBURN,
|
||||
[AI_HEAL_POISON] = STRINGID_PKMNSITEMCUREDPOISON,
|
||||
[AI_HEAL_SLEEP] = STRINGID_PKMNSITEMWOKEIT
|
||||
};
|
||||
// const u16 gTrainerItemCuredStatusStringIds[] =
|
||||
// {
|
||||
// [AI_HEAL_CONFUSION] = STRINGID_PKMNSITEMSNAPPEDOUT,
|
||||
// [AI_HEAL_PARALYSIS] = STRINGID_PKMNSITEMCUREDPARALYSIS,
|
||||
// [AI_HEAL_FREEZE] = STRINGID_PKMNSITEMDEFROSTEDIT,
|
||||
// [AI_HEAL_BURN] = STRINGID_PKMNSITEMHEALEDBURN,
|
||||
// [AI_HEAL_POISON] = STRINGID_PKMNSITEMCUREDPOISON,
|
||||
// [AI_HEAL_SLEEP] = STRINGID_PKMNSITEMWOKEIT
|
||||
// };
|
||||
|
||||
const u16 gBerryEffectStringIds[] =
|
||||
{
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
#include "trainer_pokemon_sprites.h"
|
||||
#include "field_specials.h"
|
||||
#include "battle.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_message.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_ai_script_commands.h"
|
||||
#include "battle_scripts.h"
|
||||
#include "reshow_battle_screen.h"
|
||||
#include "battle_controllers.h"
|
||||
@@ -1935,6 +1936,14 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
|
||||
}
|
||||
#undef BENEFITS_FROM_LEEK
|
||||
|
||||
s32 GetCritHitChance(s32 critChanceIndex)
|
||||
{
|
||||
if (critChanceIndex < 0)
|
||||
return -1;
|
||||
else
|
||||
return sCriticalHitChance[critChanceIndex];
|
||||
}
|
||||
|
||||
static void Cmd_critcalc(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "field_weather.h"
|
||||
#include "event_data.h"
|
||||
#include "battle.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_dynamax.h"
|
||||
#include "battle_interface.h"
|
||||
@@ -19,7 +21,6 @@
|
||||
#include "malloc.h"
|
||||
#include "constants/battle_anim.h"
|
||||
#include "battle_controllers.h"
|
||||
#include "battle_ai_script_commands.h"
|
||||
#include "constants/battle.h"
|
||||
#include "constants/moves.h"
|
||||
#include "constants/items.h"
|
||||
@@ -30,11 +31,6 @@
|
||||
#include "constants/battle_move_effects.h"
|
||||
#include "constants/battle_script_commands.h"
|
||||
|
||||
// TODO: remove when battle_ai_util
|
||||
#define AI_THINKING_STRUCT (gBattleResources->ai)
|
||||
#define BATTLE_HISTORY (gBattleResources->battleHistory)
|
||||
|
||||
|
||||
static bool32 TryRemoveScreens(u32 battler);
|
||||
static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler);
|
||||
static u32 GetFlingPowerFromItemId(u32 itemId);
|
||||
@@ -6860,37 +6856,72 @@ const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES]
|
||||
#undef ______
|
||||
#undef X
|
||||
|
||||
u8 GetMoveTarget(u16 move, u8 setTarget)
|
||||
|
||||
|
||||
|
||||
u32 SetRandomTarget(u32 battler)
|
||||
{
|
||||
u32 target;
|
||||
static const u8 targets[2][2] =
|
||||
{
|
||||
[B_SIDE_PLAYER] = {B_POSITION_OPPONENT_LEFT, B_POSITION_OPPONENT_RIGHT},
|
||||
[B_SIDE_OPPONENT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT},
|
||||
};
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
target = GetBattlerAtPosition(targets[GetBattlerSide(battler)][Random() % 2]);
|
||||
if (!IsBattlerAlive(target))
|
||||
target ^= BIT_FLANK;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = GetBattlerAtPosition(targets[GetBattlerSide(battler)][0]);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
u32 GetMoveTarget(u16 move, u8 setTarget)
|
||||
{
|
||||
u8 targetBattler = 0;
|
||||
u8 moveTarget;
|
||||
u8 side;
|
||||
u32 moveTarget, side;
|
||||
|
||||
if (setTarget != NO_TARGET_OVERRIDE)
|
||||
moveTarget = setTarget - 1;
|
||||
else
|
||||
moveTarget = gMovesInfo[move].target;
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
|
||||
|
||||
// Special cases
|
||||
if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||||
moveTarget = MOVE_TARGET_USER;
|
||||
|
||||
switch (moveTarget)
|
||||
{
|
||||
case MOVE_TARGET_SELECTED:
|
||||
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
|
||||
if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp)
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
|
||||
{
|
||||
targetBattler = gSideTimers[side].followmeTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
side = GetBattlerSide(gBattlerAttacker);
|
||||
do
|
||||
{
|
||||
targetBattler = Random() % gBattlersCount;
|
||||
} while (targetBattler == gBattlerAttacker || side == GetBattlerSide(targetBattler) || gAbsentBattlerFlags & gBitTable[targetBattler]);
|
||||
targetBattler = SetRandomTarget(gBattlerAttacker);
|
||||
if (gMovesInfo[move].type == TYPE_ELECTRIC
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_LIGHTNING_ROD, 0, 0)
|
||||
&& gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD)
|
||||
&& IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_LIGHTNING_ROD)
|
||||
&& GetBattlerAbility(targetBattler) != ABILITY_LIGHTNING_ROD)
|
||||
{
|
||||
targetBattler ^= BIT_FLANK;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
gSpecialStatuses[targetBattler].lightningRodRedirected = 1;
|
||||
gSpecialStatuses[targetBattler].lightningRodRedirected = TRUE;
|
||||
}
|
||||
else if (gMovesInfo[move].type == TYPE_WATER
|
||||
&& IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_STORM_DRAIN)
|
||||
&& GetBattlerAbility(targetBattler) != ABILITY_STORM_DRAIN)
|
||||
{
|
||||
targetBattler ^= BIT_FLANK;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
gSpecialStatuses[targetBattler].stormDrainRedirected = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -6898,40 +6929,30 @@ u8 GetMoveTarget(u16 move, u8 setTarget)
|
||||
case MOVE_TARGET_BOTH:
|
||||
case MOVE_TARGET_FOES_AND_ALLY:
|
||||
case MOVE_TARGET_OPPONENTS_FIELD:
|
||||
targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE);
|
||||
if (gAbsentBattlerFlags & gBitTable[targetBattler])
|
||||
targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)));
|
||||
if (!IsBattlerAlive(targetBattler))
|
||||
targetBattler ^= BIT_FLANK;
|
||||
break;
|
||||
case MOVE_TARGET_RANDOM:
|
||||
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
|
||||
if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp)
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
|
||||
targetBattler = gSideTimers[side].followmeTarget;
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM)
|
||||
{
|
||||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|
||||
{
|
||||
if (Random() & 1)
|
||||
targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
else
|
||||
targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Random() & 1)
|
||||
targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||||
else
|
||||
targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||
}
|
||||
if (gAbsentBattlerFlags & gBitTable[targetBattler])
|
||||
targetBattler ^= BIT_FLANK;
|
||||
}
|
||||
targetBattler = SetRandomTarget(gBattlerAttacker);
|
||||
else
|
||||
targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE);
|
||||
targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)));
|
||||
break;
|
||||
case MOVE_TARGET_USER_OR_SELECTED:
|
||||
case MOVE_TARGET_USER:
|
||||
default:
|
||||
targetBattler = gBattlerAttacker;
|
||||
break;
|
||||
case MOVE_TARGET_ALLY:
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)))
|
||||
targetBattler = BATTLE_PARTNER(gBattlerAttacker);
|
||||
else
|
||||
targetBattler = gBattlerAttacker;
|
||||
break;
|
||||
}
|
||||
|
||||
*(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler;
|
||||
@@ -7202,6 +7223,11 @@ u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct Addi
|
||||
return secondaryEffectChance;
|
||||
}
|
||||
|
||||
bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect)
|
||||
{
|
||||
return additionalEffect->chance == 0 || CalcSecondaryEffectChance(battler, battlerAbility, additionalEffect) >= 100;
|
||||
}
|
||||
|
||||
bool32 MoveHasAdditionalEffect(u32 move, u32 moveEffect)
|
||||
{
|
||||
u32 i;
|
||||
@@ -9063,6 +9089,30 @@ s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType,
|
||||
}
|
||||
}
|
||||
|
||||
bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++)
|
||||
{
|
||||
if (gMovesInfo[move].additionalEffects[i].moveEffect == moveEffect
|
||||
&& gMovesInfo[move].additionalEffects[i].self == TRUE)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 MoveHasAdditionalEffectWithChance(u32 move, u32 moveEffect, u32 chance)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++)
|
||||
{
|
||||
if (gMovesInfo[move].additionalEffects[i].moveEffect == moveEffect
|
||||
&& gMovesInfo[move].additionalEffects[i].chance == chance)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument)
|
||||
{
|
||||
return (gMovesInfo[move].argument == argument) && MoveHasAdditionalEffectSelf(move, moveEffect);
|
||||
@@ -9345,6 +9395,14 @@ u8 IsMonDisobedient(void)
|
||||
}
|
||||
}
|
||||
|
||||
// for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once
|
||||
s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef)
|
||||
{
|
||||
return DoMoveDamageCalcVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, FALSE, FALSE,
|
||||
typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef);
|
||||
}
|
||||
|
||||
static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defType, u32 battlerAtk, bool32 recordAbilities)
|
||||
{
|
||||
uq4_12_t mod = GetTypeModifier(moveType, defType);
|
||||
@@ -9506,16 +9564,25 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk,
|
||||
return modifier;
|
||||
}
|
||||
|
||||
bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect)
|
||||
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++)
|
||||
uq4_12_t modifier = UQ_4_12(1.0);
|
||||
u8 moveType = gMovesInfo[move].type;
|
||||
|
||||
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
|
||||
{
|
||||
if (gMovesInfo[move].additionalEffects[i].moveEffect == moveEffect
|
||||
&& gMovesInfo[move].additionalEffects[i].self == TRUE)
|
||||
return TRUE;
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, 0, gSpeciesInfo[speciesDef].types[0], 0, FALSE);
|
||||
if (gSpeciesInfo[speciesDef].types[1] != gSpeciesInfo[speciesDef].types[0])
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, 0, gSpeciesInfo[speciesDef].types[1], 0, FALSE);
|
||||
|
||||
if (moveType == TYPE_GROUND && abilityDef == ABILITY_LEVITATE && !(gFieldStatuses & STATUS_FIELD_GRAVITY))
|
||||
modifier = UQ_4_12(0.0);
|
||||
if (abilityDef == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gMovesInfo[move].power)
|
||||
modifier = UQ_4_12(0.0);
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
UpdateMoveResultFlags(modifier);
|
||||
return modifier;
|
||||
}
|
||||
|
||||
static uq4_12_t GetInverseTypeMultiplier(uq4_12_t multiplier)
|
||||
@@ -10441,120 +10508,120 @@ bool32 CanUltraBurst(u32 battler)
|
||||
|
||||
|
||||
// battle_ai_util.c
|
||||
bool32 IsHealingMove(u32 move)
|
||||
{
|
||||
return gMovesInfo[move].healingMove;
|
||||
}
|
||||
// bool32 IsHealingMove(u32 move)
|
||||
// {
|
||||
// return gMovesInfo[move].healingMove;
|
||||
// }
|
||||
|
||||
void RecordKnownMove(u32 battlerId, u32 move)
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (BATTLE_HISTORY->usedMoves[battlerId][i] == move)
|
||||
break;
|
||||
if (BATTLE_HISTORY->usedMoves[battlerId][i] == MOVE_NONE)
|
||||
{
|
||||
BATTLE_HISTORY->usedMoves[battlerId][i] = move;
|
||||
// TODO: AI
|
||||
// AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].moves[i] = move;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// void RecordKnownMove(u32 battlerId, u32 move)
|
||||
// {
|
||||
// s32 i;
|
||||
// for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
// {
|
||||
// if (BATTLE_HISTORY->usedMoves[battlerId][i] == move)
|
||||
// break;
|
||||
// if (BATTLE_HISTORY->usedMoves[battlerId][i] == MOVE_NONE)
|
||||
// {
|
||||
// BATTLE_HISTORY->usedMoves[battlerId][i] = move;
|
||||
// // TODO: AI
|
||||
// // AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].moves[i] = move;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
s32 CountUsablePartyMons(u32 battlerId)
|
||||
{
|
||||
s32 battlerOnField1, battlerOnField2, i, ret;
|
||||
struct Pokemon *party;
|
||||
// s32 CountUsablePartyMons(u32 battlerId)
|
||||
// {
|
||||
// s32 battlerOnField1, battlerOnField2, i, ret;
|
||||
// struct Pokemon *party;
|
||||
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
party = gPlayerParty;
|
||||
else
|
||||
party = gEnemyParty;
|
||||
// if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
// party = gPlayerParty;
|
||||
// else
|
||||
// party = gEnemyParty;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))];
|
||||
}
|
||||
else // In singles there's only one battlerId by side.
|
||||
{
|
||||
battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
battlerOnField2 = gBattlerPartyIndexes[battlerId];
|
||||
}
|
||||
// if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
// {
|
||||
// battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
// battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))];
|
||||
// }
|
||||
// else // In singles there's only one battlerId by side.
|
||||
// {
|
||||
// battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
// battlerOnField2 = gBattlerPartyIndexes[battlerId];
|
||||
// }
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (i != battlerOnField1 && i != battlerOnField2
|
||||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
// ret = 0;
|
||||
// for (i = 0; i < PARTY_SIZE; i++)
|
||||
// {
|
||||
// if (i != battlerOnField1 && i != battlerOnField2
|
||||
// && GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
// && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
// && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
|
||||
// {
|
||||
// ret++;
|
||||
// }
|
||||
// }
|
||||
|
||||
return ret;
|
||||
}
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
bool32 IsAiVsAiBattle(void)
|
||||
{
|
||||
// TODO: Flag?
|
||||
return (B_FLAG_AI_VS_AI_BATTLE && FlagGet(B_FLAG_AI_VS_AI_BATTLE));
|
||||
}
|
||||
// bool32 IsAiVsAiBattle(void)
|
||||
// {
|
||||
// // TODO: Flag?
|
||||
// return (B_FLAG_AI_VS_AI_BATTLE && FlagGet(B_FLAG_AI_VS_AI_BATTLE));
|
||||
// }
|
||||
|
||||
void RecordLastUsedMoveBy(u32 battlerId, u32 move)
|
||||
{
|
||||
u8 *index = &BATTLE_HISTORY->moveHistoryIndex[battlerId];
|
||||
// void RecordLastUsedMoveBy(u32 battlerId, u32 move)
|
||||
// {
|
||||
// u8 *index = &BATTLE_HISTORY->moveHistoryIndex[battlerId];
|
||||
|
||||
if (++(*index) >= AI_MOVE_HISTORY_COUNT)
|
||||
*index = 0;
|
||||
BATTLE_HISTORY->moveHistory[battlerId][*index] = move;
|
||||
}
|
||||
// if (++(*index) >= AI_MOVE_HISTORY_COUNT)
|
||||
// *index = 0;
|
||||
// BATTLE_HISTORY->moveHistory[battlerId][*index] = move;
|
||||
// }
|
||||
|
||||
bool32 BattlerHasAi(u32 battlerId)
|
||||
{
|
||||
switch (GetBattlerPosition(battlerId))
|
||||
{
|
||||
case B_POSITION_PLAYER_LEFT:
|
||||
if (IsAiVsAiBattle())
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
case B_POSITION_OPPONENT_LEFT:
|
||||
return TRUE;
|
||||
case B_POSITION_PLAYER_RIGHT:
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) || IsAiVsAiBattle())
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
case B_POSITION_OPPONENT_RIGHT:
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// bool32 BattlerHasAi(u32 battlerId)
|
||||
// {
|
||||
// switch (GetBattlerPosition(battlerId))
|
||||
// {
|
||||
// case B_POSITION_PLAYER_LEFT:
|
||||
// if (IsAiVsAiBattle())
|
||||
// return TRUE;
|
||||
// default:
|
||||
// return FALSE;
|
||||
// case B_POSITION_OPPONENT_LEFT:
|
||||
// return TRUE;
|
||||
// case B_POSITION_PLAYER_RIGHT:
|
||||
// if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) || IsAiVsAiBattle())
|
||||
// return TRUE;
|
||||
// else
|
||||
// return FALSE;
|
||||
// case B_POSITION_OPPONENT_RIGHT:
|
||||
// return TRUE;
|
||||
// }
|
||||
// }
|
||||
|
||||
void ClearBattlerItemEffectHistory(u32 battlerId)
|
||||
{
|
||||
BATTLE_HISTORY->itemEffects[battlerId] = 0;
|
||||
}
|
||||
// void ClearBattlerItemEffectHistory(u32 battlerId)
|
||||
// {
|
||||
// BATTLE_HISTORY->itemEffects[battlerId] = 0;
|
||||
// }
|
||||
|
||||
// move checks
|
||||
bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect)
|
||||
{
|
||||
if (ability == ABILITY_OVERCOAT
|
||||
|| (B_POWDER_GRASS >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|
||||
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
// // move checks
|
||||
// bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect)
|
||||
// {
|
||||
// if (ability == ABILITY_OVERCOAT
|
||||
// || (B_POWDER_GRASS >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|
||||
// || holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
// return FALSE;
|
||||
// return TRUE;
|
||||
// }
|
||||
|
||||
void RecordAllMoves(u32 battler)
|
||||
{
|
||||
// TODO: AI
|
||||
// memcpy(AI_PARTY->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].moves, gBattleMons[battler].moves, MAX_MON_MOVES * sizeof(u16));
|
||||
}
|
||||
// void RecordAllMoves(u32 battler)
|
||||
// {
|
||||
// // TODO: AI
|
||||
// // memcpy(AI_PARTY->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].moves, gBattleMons[battler].moves, MAX_MON_MOVES * sizeof(u16));
|
||||
// }
|
||||
|
||||
|
||||
// end battle_ai_util.c
|
||||
@@ -9922,6 +9922,7 @@ static const struct TrainerMon sParty_TestBattle[] = {
|
||||
{
|
||||
.lvl = 14,
|
||||
.species = SPECIES_VENUSAUR,
|
||||
.heldItem = ITEM_VENUSAURITE,
|
||||
},
|
||||
{
|
||||
.lvl = 14,
|
||||
|
||||
@@ -1219,6 +1219,24 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
|
||||
if (IsSelectedMonNotEgg((u8 *)slotPtr))
|
||||
TryEnterMonForMinigame(taskId, (u8)*slotPtr);
|
||||
break;
|
||||
case PARTY_ACTION_CHOOSE_FAINTED_MON:
|
||||
{
|
||||
u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr);
|
||||
if (GetMonData(&gPlayerParty[*slotPtr], MON_DATA_HP) > 0
|
||||
|| GetMonData(&gPlayerParty[*slotPtr], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG
|
||||
|| ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)))
|
||||
{
|
||||
// Can't select if egg, alive, or doesn't belong to you
|
||||
PlaySE(SE_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySE(SE_SELECT);
|
||||
gSelectedMonPartyId = partyId;
|
||||
Task_ClosePartyMenu(taskId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case PARTY_ACTION_ABILITY_PREVENTS:
|
||||
case PARTY_ACTION_SWITCHING:
|
||||
|
||||
160
src/pokemon.c
160
src/pokemon.c
@@ -3125,30 +3125,28 @@ static u8 CopyMonToPC(struct Pokemon *mon)
|
||||
return MON_CANT_GIVE;
|
||||
}
|
||||
|
||||
u8 CalculatePlayerPartyCount(void)
|
||||
u8 CalculatePartyCount(struct Pokemon *party)
|
||||
{
|
||||
gPlayerPartyCount = 0;
|
||||
u32 partyCount = 0;
|
||||
|
||||
while (gPlayerPartyCount < PARTY_SIZE
|
||||
&& GetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
|
||||
while (partyCount < PARTY_SIZE
|
||||
&& GetMonData(&party[partyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
|
||||
{
|
||||
gPlayerPartyCount++;
|
||||
partyCount++;
|
||||
}
|
||||
|
||||
return partyCount;
|
||||
}
|
||||
|
||||
u8 CalculatePlayerPartyCount(void)
|
||||
{
|
||||
gPlayerPartyCount = CalculatePartyCount(gPlayerParty);
|
||||
return gPlayerPartyCount;
|
||||
}
|
||||
|
||||
|
||||
u8 CalculateEnemyPartyCount(void)
|
||||
{
|
||||
gEnemyPartyCount = 0;
|
||||
|
||||
while (gEnemyPartyCount < PARTY_SIZE
|
||||
&& GetMonData(&gEnemyParty[gEnemyPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
|
||||
{
|
||||
gEnemyPartyCount++;
|
||||
}
|
||||
|
||||
gEnemyPartyCount = CalculatePartyCount(gEnemyParty);
|
||||
return gEnemyPartyCount;
|
||||
}
|
||||
|
||||
@@ -3369,6 +3367,55 @@ void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex)
|
||||
mon->ppBonuses &= gPPUpClearMask[moveIndex];
|
||||
}
|
||||
|
||||
void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst)
|
||||
{
|
||||
s32 i;
|
||||
u8 nickname[POKEMON_NAME_BUFFER_SIZE];
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
dst->moves[i] = GetMonData(src, MON_DATA_MOVE1 + i, NULL);
|
||||
dst->pp[i] = GetMonData(src, MON_DATA_PP1 + i, NULL);
|
||||
}
|
||||
|
||||
dst->species = GetMonData(src, MON_DATA_SPECIES, NULL);
|
||||
dst->item = GetMonData(src, MON_DATA_HELD_ITEM, NULL);
|
||||
dst->ppBonuses = GetMonData(src, MON_DATA_PP_BONUSES, NULL);
|
||||
dst->friendship = GetMonData(src, MON_DATA_FRIENDSHIP, NULL);
|
||||
dst->experience = GetMonData(src, MON_DATA_EXP, NULL);
|
||||
dst->hpIV = GetMonData(src, MON_DATA_HP_IV, NULL);
|
||||
dst->attackIV = GetMonData(src, MON_DATA_ATK_IV, NULL);
|
||||
dst->defenseIV = GetMonData(src, MON_DATA_DEF_IV, NULL);
|
||||
dst->speedIV = GetMonData(src, MON_DATA_SPEED_IV, NULL);
|
||||
dst->spAttackIV = GetMonData(src, MON_DATA_SPATK_IV, NULL);
|
||||
dst->spDefenseIV = GetMonData(src, MON_DATA_SPDEF_IV, NULL);
|
||||
dst->personality = GetMonData(src, MON_DATA_PERSONALITY, NULL);
|
||||
dst->status1 = GetMonData(src, MON_DATA_STATUS, NULL);
|
||||
dst->level = GetMonData(src, MON_DATA_LEVEL, NULL);
|
||||
dst->hp = GetMonData(src, MON_DATA_HP, NULL);
|
||||
dst->maxHP = GetMonData(src, MON_DATA_MAX_HP, NULL);
|
||||
dst->attack = GetMonData(src, MON_DATA_ATK, NULL);
|
||||
dst->defense = GetMonData(src, MON_DATA_DEF, NULL);
|
||||
dst->speed = GetMonData(src, MON_DATA_SPEED, NULL);
|
||||
dst->spAttack = GetMonData(src, MON_DATA_SPATK, NULL);
|
||||
dst->spDefense = GetMonData(src, MON_DATA_SPDEF, NULL);
|
||||
dst->abilityNum = GetMonData(src, MON_DATA_ABILITY_NUM, NULL);
|
||||
dst->otId = GetMonData(src, MON_DATA_OT_ID, NULL);
|
||||
dst->type1 = gSpeciesInfo[dst->species].types[0];
|
||||
dst->type2 = gSpeciesInfo[dst->species].types[1];
|
||||
dst->type3 = TYPE_MYSTERY;
|
||||
dst->ability = GetAbilityBySpecies(dst->species, dst->abilityNum);
|
||||
GetMonData(src, MON_DATA_NICKNAME, nickname);
|
||||
StringCopy_Nickname(dst->nickname, nickname);
|
||||
GetMonData(src, MON_DATA_OT_NAME, dst->otName);
|
||||
|
||||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||||
dst->statStages[i] = DEFAULT_STAT_STAGE;
|
||||
|
||||
dst->status2 = 0;
|
||||
}
|
||||
|
||||
|
||||
bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
|
||||
{
|
||||
return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, FALSE);
|
||||
@@ -5052,6 +5099,91 @@ bool8 TryIncrementMonLevel(struct Pokemon *mon)
|
||||
}
|
||||
}
|
||||
|
||||
static const u16 sUniversalMoves[] =
|
||||
{
|
||||
MOVE_BIDE,
|
||||
MOVE_FRUSTRATION,
|
||||
MOVE_HIDDEN_POWER,
|
||||
MOVE_MIMIC,
|
||||
MOVE_NATURAL_GIFT,
|
||||
MOVE_RAGE,
|
||||
MOVE_RETURN,
|
||||
MOVE_SECRET_POWER,
|
||||
MOVE_SUBSTITUTE,
|
||||
MOVE_TERA_BLAST,
|
||||
};
|
||||
|
||||
u8 CanLearnTeachableMove(u16 species, u16 move)
|
||||
{
|
||||
if (species == SPECIES_EGG)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
else if (species == SPECIES_MEW)
|
||||
{
|
||||
switch (move)
|
||||
{
|
||||
case MOVE_BADDY_BAD:
|
||||
case MOVE_BOUNCY_BUBBLE:
|
||||
case MOVE_BUZZY_BUZZ:
|
||||
case MOVE_DRAGON_ASCENT:
|
||||
case MOVE_FLOATY_FALL:
|
||||
case MOVE_FREEZY_FROST:
|
||||
case MOVE_GLITZY_GLOW:
|
||||
case MOVE_RELIC_SONG:
|
||||
case MOVE_SAPPY_SEED:
|
||||
case MOVE_SECRET_SWORD:
|
||||
case MOVE_SIZZLY_SLIDE:
|
||||
case MOVE_SPARKLY_SWIRL:
|
||||
case MOVE_SPLISHY_SPLASH:
|
||||
case MOVE_VOLT_TACKLE:
|
||||
case MOVE_ZIPPY_ZAP:
|
||||
return FALSE;
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 i, j;
|
||||
const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species);
|
||||
for (i = 0; i < ARRAY_COUNT(sUniversalMoves); i++)
|
||||
{
|
||||
if (sUniversalMoves[i] == move)
|
||||
{
|
||||
if (!gSpeciesInfo[species].tmIlliterate)
|
||||
{
|
||||
if (move == MOVE_TERA_BLAST && GET_BASE_SPECIES_ID(species) == SPECIES_TERAPAGOS)
|
||||
return FALSE;
|
||||
if (GET_BASE_SPECIES_ID(species) == SPECIES_PYUKUMUKU && (move == MOVE_HIDDEN_POWER || move == MOVE_RETURN || move == MOVE_FRUSTRATION))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species);
|
||||
|
||||
if (P_TM_LITERACY < GEN_6)
|
||||
return FALSE;
|
||||
|
||||
for (j = 0; j < MAX_LEVEL_UP_MOVES && learnset[j].move != LEVEL_UP_MOVE_END; j++)
|
||||
{
|
||||
if (learnset[j].move == move)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++)
|
||||
{
|
||||
if (teachableLearnset[i] == move)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
u32 CanMonLearnTMHM(struct Pokemon *mon, u8 tm)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
.include "src/mail.o"
|
||||
.include "src/menu_helpers.o"
|
||||
.include "src/region_map.o"
|
||||
.include "src/battle_ai_script_commands.o"
|
||||
.include "src/battle_ai_main.o"
|
||||
.include "src/fldeff_rocksmash.o"
|
||||
.include "src/field_specials.o"
|
||||
.include "src/battle_records.o"
|
||||
|
||||
Reference in New Issue
Block a user