Merge pull request #6 from cawtds/update-trainers-and-ai

Updated Trainers, AI and fixed 1v2 double battles
This commit is contained in:
cawtds 2024-05-12 22:41:19 +02:00 committed by GitHub
commit bb77330a35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 16974 additions and 12219 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,6 @@ PewterCity_Gym_Text_BrockIntro::
.string "Fine, then!\n"
.string "Show me your best!{PLAY_BGM}{MUS_ENCOUNTER_GYM_LEADER}$"
@ NOTE: This defeat text actually causes a buffer overflow. It's too long for the gDisplayedStringBattle
@ buffer that it's put into, and it stomps all over the gBattleTextBuffs after, as well as the otherwise
@ unused array after that, sFlickerArray. Perhaps that's the reason why said array exists.
PewterCity_Gym_Text_BrockDefeat::
.string "I took you for granted, and so\n"
.string "I lost.\p"

View File

@ -102,6 +102,7 @@ ViridianCity_Mart_Items::
.2byte ITEM_MEGA_RING
.2byte ITEM_CHARIZARDITE_X
.2byte ITEM_CHARIZARDITE_Y
.2byte ITEM_SILPH_SCOPE
.2byte ITEM_NONE
release
end

View File

@ -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
@ -502,165 +553,163 @@ struct BattleStruct
{
u8 turnEffectsTracker;
u8 turnEffectsBattlerId;
u8 filler2; // unused
u8 turnCountersTracker;
u16 wrappedMove[MAX_BATTLERS_COUNT];
u8 moveTarget[MAX_BATTLERS_COUNT];
u16 moveTarget[MAX_BATTLERS_COUNT];
u32 expShareExpValue;
u32 expValue;
u8 expGettersOrder[PARTY_SIZE]; // First battlers which were sent out, then via exp-share
u8 expGetterMonId;
u8 field_11; // unused
u8 expOrderId:3;
u8 expGetterBattlerId:2;
u8 teamGotExpMsgPrinted:1; // The 'Rest of your team got msg' has been printed.
u8 givenExpMons; // Bits for enemy party's pokemon that gave exp to player's party.
u8 expSentInMons; // As bits for player party mons - not including exp share mons.
u8 wildVictorySong;
u8 dynamicMoveType;
u8 wrappedBy[MAX_BATTLERS_COUNT];
u8 focusPunchBattlerId;
u8 focusPunchBattlers; // as bits
u8 battlerPreventingSwitchout;
u8 moneyMultiplier;
u8 moneyMultiplier:6;
u8 moneyMultiplierItem:1;
u8 moneyMultiplierMove:1;
u8 savedTurnActionNumber;
u8 switchInAbilitiesCounter;
u8 faintedActionsState;
u8 faintedActionsBattlerId;
// balign 2
u32 expValue;
u8 scriptPartyIdx; // for printing the nickname
u8 sentInPokes;
bool8 selectionScriptFinished[MAX_BATTLERS_COUNT];
u8 battlerPartyIndexes[MAX_BATTLERS_COUNT];
u8 monToSwitchIntoId[MAX_BATTLERS_COUNT];
u8 battlerPartyOrders[MAX_BATTLERS_COUNT][3];
u8 battlerPartyOrders[MAX_BATTLERS_COUNT][PARTY_SIZE / 2];
u8 runTries;
u8 caughtMonNick[POKEMON_NAME_LENGTH + 1];
u8 field_78; // unused
u8 safariRockThrowCounter; // safariGoNearCounter in pokeemerald
u8 safariBaitThrowCounter; // safariPkblThrowCounter in pokeemerald
u8 safariEscapeFactor;
u8 safariCatchFactor;
u8 linkBattleVsSpriteId_V;
u8 linkBattleVsSpriteId_S;
u8 linkBattleVsSpriteId_V; // The letter "V"
u8 linkBattleVsSpriteId_S; // The letter "S"
u8 formToChangeInto;
u8 chosenMovePositions[MAX_BATTLERS_COUNT];
u8 stateIdAfterSelScript[MAX_BATTLERS_COUNT];
u8 field_88; // unused
u8 field_89; // unused
u8 field_8A; // unused
u8 playerPartyIdx;
u8 field_8C; // unused
u8 field_8D; // unused
u8 prevSelectedPartySlot;
u8 stringMoveType;
u8 expGetterBattlerId;
u8 field_90; // unused
u8 absentBattlerFlags;
u8 AI_monToSwitchIntoId[2];
u8 simulatedInputState[4]; // used by Oak/Old Man/Pokedude controllers
u8 lastTakenMove[MAX_BATTLERS_COUNT * 2 * 2]; // ask gamefreak why they declared it that way
u16 hpOnSwitchout[2];
u8 field_93; // related to choosing pokemon?
u8 simulatedInputState[4]; // used by Oak/Old Man/Pokedude controllers, Wally States/Frames in pokeemerald
u16 lastTakenMove[MAX_BATTLERS_COUNT]; // Last move that a battler was hit with.
u16 hpOnSwitchout[NUM_BATTLE_SIDES];
u32 savedBattleTypeFlags;
u16 abilityPreventingSwitchout;
u8 hpScale;
u16 savedBattleTypeFlags;
void (*savedCallback)(void);
u16 synchronizeMoveEffect;
u8 multiplayerId;
u8 overworldWeatherDone;
u8 atkCancellerTracker;
bool8 anyMonHasTransformed;
void (*savedCallback)(void);
u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle
u8 chosenItem[MAX_BATTLERS_COUNT]; // why is this an u8?
u8 AI_itemType[2];
u8 AI_itemFlags[2];
u16 chosenItem[MAX_BATTLERS_COUNT];
u16 choicedMove[MAX_BATTLERS_COUNT];
u16 changedItems[MAX_BATTLERS_COUNT];
u8 intimidateBattler;
u8 switchInItemsCounter;
u8 field_DA; // battle tower related
u8 turnSideTracker;
u8 fillerDC[0xDF-0xDC];
u8 givenExpMons;
u8 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
u8 wishPerishSongState;
u8 wishPerishSongBattlerId;
u8 lastAttackerToFaintOpponent;
// align 4
u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker]
union {
struct LinkBattlerHeader linkBattlerHeader;
struct MultiPartnerMenuPokemon multiBattleMons[3];
} multiBuffer;
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
u8 padding_1E4[0x14];
// pokeemerald
u8 isSkyBattle:1;
u8 wishPerishSongState;
u8 wishPerishSongBattlerId;
u8 overworldWeatherDone:1;
u8 startingStatusDone:1;
u8 isAtkCancelerForCalledMove:1; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice.
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 beatUpSlot:3;
u8 pledgeMove:1;
u8 terrainDone:1;
u8 startingStatus; // status to apply at battle start. defined in constants/battle.h
u8 startingStatusTimer;
u8 atkCancellerTracker;
u8 AI_monToSwitchIntoId[MAX_BATTLERS_COUNT];
u8 alreadyStatusedMoveAttempt; // As bits for battlers; For example when using Thunder Wave on an already paralyzed Pokémon.
u8 magnitudeBasePower;
u8 presentBasePower;
u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used.
u8 lastMoveFailed; // as bits for each battler, for the sake of Stomping Tantrum
bool8 ateBoost[MAX_BATTLERS_COUNT];
u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
struct Illusion illusion[MAX_BATTLERS_COUNT];
u8 trainerSlideFirstSTABMoveMsgState:2;
u8 blunderPolicy:1; // should blunder policy activate
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies.
u8 enduredDamage;
u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon.
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 moveEffect2; // For Knock Off
u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages
u8 stickySyrupdBy[MAX_BATTLERS_COUNT];
u8 moneyMultiplierMove:1;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
u8 roostTypes[MAX_BATTLERS_COUNT][2];
u8 savedBattlerTarget;
u8 ateBerry[2]; // array id determined by side, each party pokemon as bit
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 ateBoost[MAX_BATTLERS_COUNT];
u8 activeAbilityPopUps; // as bits for each battler
u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler
bool8 throwingPokeBall;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
struct DynamaxData dynamax;
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
bool8 allowedToChangeFormInWeather[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side, used by Ice Face.
u8 startingStatus; // status to apply at battle start. defined in constants/battle.h
u8 startingStatusTimer;
u8 transformZeroToHero[NUM_BATTLE_SIDES];
u8 supersweetSyrup[NUM_BATTLE_SIDES];
u8 intrepidSwordBoost[NUM_BATTLE_SIDES];
u8 dauntlessShieldBoost[NUM_BATTLE_SIDES];
const u8 *trainerSlideMsg;
bool8 trainerSlideLowHpMsgDone;
u8 introState;
u8 ateBerry[2]; // array id determined by side, each party pokemon as bit
u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages
u8 lastMoveFailed; // as bits for each battler, for the sake of Stomping Tantrum
u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct
u16 tracedAbility[MAX_BATTLERS_COUNT];
u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk and Anger Shell.
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once.
bool8 spriteIgnore0Hp;
u8 moneyMultiplierItem:1;
u8 expGettersOrder[PARTY_SIZE]; // First battlers which were sent out, then via exp-share
u32 expShareExpValue;
u8 expOrderId:3;
u8 expSentInMons; // As bits for player party mons - not including exp share mons.
u8 teamGotExpMsgPrinted:1; // The 'Rest of your team got msg' has been printed.
u8 roostTypes[MAX_BATTLERS_COUNT][2];
u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct
u8 attackerBeforeBounce:2;
bool8 hitSwitchTargetFailed:1;
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.
u8 forcedSwitch:4; // For each battler
u8 alreadyStatusedMoveAttempt; // As bits for battlers; For example when using Thunder Wave on an already paralyzed Pokémon.
u8 soulheartBattlerId;
const u8 *trainerSlideMsg;
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 quickClawBattlerId;
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
u8 focusPunchBattlers; // as bits
u8 quickClawRandom[MAX_BATTLERS_COUNT];
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
bool8 throwingPokeBall;
struct Illusion illusion[MAX_BATTLERS_COUNT];
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];
// pokeemerald unknown use
u8 field_93; // related to choosing pokemon? probably related to recording
u8 soulheartBattlerId;
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once.
u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used.
u16 moveEffect2; // For Knock Off
u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon.
u8 quickClawBattlerId;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
u8 forcedSwitch:4; // For each battler
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 bouncedMoveIsUsed:1;
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.
u8 attackerBeforeBounce:2;
u8 beatUpSlot:3;
bool8 hitSwitchTargetFailed:1;
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side, used by Ice Face.
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.
u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies.
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;
u8 trainerSlideFirstSTABMoveMsgState:2;
u8 trainerSlidePlayerMonUnaffectedMsgState:2;
u8 trainerSlideHalfHpMsgDone:1;
u8 trainerSlideMegaEvolutionMsgDone:1;
u8 trainerSlideZMoveMsgDone:1;
u8 trainerSlideBeforeFirstTurnMsgDone:1;
u8 trainerSlideDynamaxMsgDone:1;
u8 pledgeMove:1;
u8 isSkyBattle:1;
u32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
u32 aiDelayFrames; // Number of frames it took to choose an action.
u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 enduredDamage;
u8 transformZeroToHero[NUM_BATTLE_SIDES];
u8 stickySyrupdBy[MAX_BATTLERS_COUNT];
u8 intrepidSwordBoost[NUM_BATTLE_SIDES];
u8 dauntlessShieldBoost[NUM_BATTLE_SIDES];
u8 supersweetSyrup[NUM_BATTLE_SIDES];
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
u8 quickClawRandom[MAX_BATTLERS_COUNT];
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
// pokefirered
u8 field_DA; // battle tower related
u8 lastAttackerToFaintOpponent;
};
extern struct BattleStruct *gBattleStruct;
@ -824,6 +873,9 @@ struct BattleHealthboxInfo
u8 triedShinyMonAnim : 1; // x80
u8 finishedShinyMonAnim : 1; // x1
u8 opponentDrawPartyStatusSummaryDelay : 5; // x2
u8 bgmRestored:1;
u8 waitForCry:1;
u8 healthboxSlideInStarted:1;
u8 healthboxBounceSpriteId;
u8 battlerBounceSpriteId;
u8 animationState;
@ -865,7 +917,7 @@ extern u8 *gLinkBattleRecvBuffer;
struct MonSpritesGfx
{
void *firstDecompressed; // ptr to the decompressed sprite of the first pokemon
void *sprites[MAX_BATTLERS_COUNT];
u8 *sprites[MAX_BATTLERS_COUNT];
struct SpriteTemplate templates[MAX_BATTLERS_COUNT];
struct SpriteFrameImage images[MAX_BATTLERS_COUNT][4];
u8 field_F4[0x80 - (4 * MAX_BATTLERS_COUNT)]; // unused, original - spritesGfx
@ -929,10 +981,7 @@ extern bool8 gTransformedShininess[MAX_BATTLERS_COUNT];
extern u8 gBattlerPositions[MAX_BATTLERS_COUNT];
extern u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
extern u8 gBattleOutcome;
extern u8 gBattleMonForms[MAX_BATTLERS_COUNT];
extern u32 gBattleControllerExecFlags;
// extern u8 gBattleBufferA[MAX_BATTLERS_COUNT][0x200];
// extern u8 gBattleBufferB[MAX_BATTLERS_COUNT][0x200];
extern u8 gActionSelectionCursor[MAX_BATTLERS_COUNT];
extern void (*gPreBattleCallback1)(void);
extern bool8 gDoingBattleAnim;
@ -942,6 +991,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 +1049,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
View 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

View File

@ -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

View File

@ -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
View 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

View File

@ -54,7 +54,6 @@ extern const u8 gStatusConditionString_IceJpn[8];
extern const u8 gStatusConditionString_ConfusionJpn[8];
extern const u8 gStatusConditionString_LoveJpn[8];
extern const u8 *const gStatusConditionStringsTable[7][2];
extern const struct TrainerMoney gTrainerMoneyTable[];
void CB2_InitBattle(void);
void BattleMainCB2(void);
@ -86,7 +85,6 @@ void SwitchPartyOrder(u32 battlerId);
void SwapTurnOrder(u8 id1, u8 id2);
void RunBattleScriptCommands_PopCallbacksStack(void);
void RunBattleScriptCommands(void);
bool8 TryRunFromBattle(u8 battler);
s8 GetMovePriority(u32 battlerId, u16 move);
s8 GetChosenMovePriority(u32 battlerId);
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect);

View File

@ -233,10 +233,10 @@ extern struct BattleMsgData *gBattleMsgDataPtr;
#define TEXT_BUFF_ARRAY_COUNT 16
extern u8 gDisplayedStringBattle[300];
extern u8 gDisplayedStringBattle[550];
extern u8 gBattleTextBuff1[TEXT_BUFF_ARRAY_COUNT];
extern u8 gBattleTextBuff2[TEXT_BUFF_ARRAY_COUNT];
extern u8 gBattleTextBuff3[TEXT_BUFF_ARRAY_COUNT];
extern u8 gBattleTextBuff3[TEXT_BUFF_ARRAY_COUNT + 13];
extern const u8 *const gBattleStringsTable[];
extern const u8 *const gStatNamesTable[];

View File

@ -22,9 +22,6 @@ struct PickupItem
u8 percentage[10];
};
void AI_CalcDmg(u8 attacker, u8 defender);
u8 TypeCalc(u16 move, u8 attacker, u8 defender);
u8 AI_TypeCalc(u16 move, u16 targetSpecies, u16 targetAbility);
u8 GetBattlerTurnOrderNum(u8 battlerId);
void SetMoveEffect(bool32 primary, u32 certain);
bool32 IsMonGettingExpSentOut(void);
@ -53,6 +50,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[];

View File

@ -6,6 +6,7 @@
extern u16 gPartnerTrainerId;
void StartWildBattle(void);
void StartDoubleWildBattle(void);
void StartRoamerBattle(void);
void StartOldManTutorialBattle(void);
void StartScriptedWildBattle(void);

View File

@ -120,7 +120,23 @@ enum
extern const struct TypePower gNaturalGiftTable[];
extern const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES];
void HandleAction_UseMove(void);
void HandleAction_Switch(void);
void HandleAction_UseItem(void);
bool8 TryRunFromBattle(u8 battler);
void HandleAction_Run(void);
void HandleAction_WatchesCarefully(void);
void HandleAction_SafariZoneBallThrow(void);
void HandleAction_ThrowBait(void);
void HandleAction_ThrowRock(void);
void HandleAction_SafariZoneRun(void);
void HandleAction_OldManBallThrow(void);
void HandleAction_TryFinish(void);
void HandleAction_NothingIsFainted(void);
void HandleAction_ActionFinished(void);
u8 GetBattlerForBattleScript(u8 caseId);
bool32 IsBattlerMarkedForControllerExec(u32 battler);
void MarkBattlerForControllerExec(u8 battlerId);
void MarkBattlerReceivedLinkData(u8 battlerId);
const u8* CancelMultiTurnMoves(u32 battler);
@ -148,9 +164,8 @@ 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
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move);
bool32 IsNeutralizingGasOnField(void);
@ -178,6 +193,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 +207,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,18 +262,11 @@ void TryClearRageAndFuryCutter(void);
bool32 CanMegaEvolve(u32 battler);
bool32 CanUltraBurst(u32 battler);
bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move);
// 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);
// end battle_ai_util.h
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);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -6,5 +6,6 @@
void AllocateBattleResources(void);
void FreeBattleResources(void);
void AdjustFriendshipOnBattleFaint(u8 bank);
void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -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
@ -211,11 +212,6 @@
#define HITMARKER_FAINTED(battler) (gBitTable[battler] << 28)
#define HITMARKER_FAINTED2(battler) ((1 << 28) << battler)
#define HITMARKER_STRING_PRINTED (1 << 29)
// TODO: old hitmarkers for compatability, remove with new battle system
#define HITMARKER_SKIP_DMG_TRACK (1 << 30)
#define HITMARKER_IGNORE_ON_AIR (1 << 31)
#define HITMARKER_IGNORE_UNDERGROUND (1 << 0)
#define HITMARKER_IGNORE_UNDERWATER (1 << 1)
// Per-side statuses that affect an entire party
@ -226,7 +222,6 @@
#define SIDE_STATUS_SAFEGUARD (1 << 5)
#define SIDE_STATUS_FUTUREATTACK (1 << 6)
#define SIDE_STATUS_MIST (1 << 8)
#define SIDE_STATUS_SPIKES_DAMAGED (1 << 9) // TODO: remove with new battle system
// (1 << 9) previously was SIDE_STATUS_SPIKES_DAMAGED
#define SIDE_STATUS_TAILWIND (1 << 10)
#define SIDE_STATUS_AURORA_VEIL (1 << 11)

View File

@ -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_SCRIPT_CHECK_BAD_MOVE (1 << 0)
#define AI_SCRIPT_CHECK_VIABILITY (1 << 1)
#define AI_SCRIPT_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

View File

@ -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
@ -82,6 +83,7 @@
#define WONDER_NEWS_BODY_TEXT_LINES 10
#define TYPE_NAME_LENGTH 6
#define ABILITY_NAME_LENGTH ((B_EXPANDED_ABILITY_NAMES == TRUE) ? 16 : 12)
#define TRAINER_NAME_LENGTH 10
#define MAX_STAMP_CARD_STAMPS 7

View File

@ -1,6 +1,8 @@
#ifndef GUARD_CONSTANTS_OPPONENTS_H
#define GUARD_CONSTANTS_OPPONENTS_H
#include "constants/battle_partner.h"
#define TRAINER_NONE 0
// Dummy trainers for all the RS trainer classes
#define TRAINER_AQUA_LEADER 1
@ -752,7 +754,7 @@
// only space for 25 additional trainers before trainer flag space overflows.
// MAX_TRAINERS_COUNT can be increased but will take up additional saveblock space
#define NUM_TRAINERS 744
#define TRAINERS_COUNT 744
#define MAX_TRAINERS_COUNT 769
#define TRAINER_PARTNER(partner) (MAX_TRAINERS_COUNT + partner)

View File

@ -288,6 +288,8 @@
#define TRAINER_CLASS_LADY 105
#define TRAINER_CLASS_PAINTER 106
#define TRAINER_CLASS_COUNT 107
#define FACILITY_CLASS_AQUA_LEADER_ARCHIE 0
#define FACILITY_CLASS_AQUA_GRUNT_M 1
#define FACILITY_CLASS_AQUA_GRUNT_F 2
@ -441,6 +443,10 @@
#define F_TRAINER_FEMALE (1 << 7)
// Trainer party defines
#define TRAINER_MON_MALE 1
#define TRAINER_MON_FEMALE 2
// All trainer parties specify the IV, level, and species for each Pokémon in the
// party. Some trainer parties also specify held items and custom moves for each
// Pokémon.

View File

@ -18,6 +18,8 @@ struct MonCoords
#define MON_COORDS_SIZE(width, height)(DIV_ROUND_UP(width, 8) << 4 | DIV_ROUND_UP(height, 8))
#define GET_MON_COORDS_WIDTH(size)((size >> 4) * 8)
#define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8)
#define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25))
#define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed})
#define MAX_TRAINER_ITEMS 4
@ -28,76 +30,51 @@ enum {
BATTLER_AFFINE_RETURN,
};
struct TrainerMonNoItemDefaultMoves
struct TrainerMon
{
u16 iv;
u8 lvl;
u16 species;
};
struct TrainerMonItemDefaultMoves
{
u16 iv;
u8 lvl;
const u8 *nickname;
const u8 *ev;
u32 iv;
u16 moves[4];
u16 species;
u16 heldItem;
};
struct TrainerMonNoItemCustomMoves
{
u16 iv;
u16 ability;
u8 lvl;
u16 species;
u16 moves[MAX_MON_MOVES];
u8 ball;
u8 friendship;
u8 nature:5;
bool8 gender:2;
bool8 isShiny:1;
u8 dynamaxLevel:4;
bool8 gigantamaxFactor:1;
bool8 shouldDynamax:1;
bool8 shouldTerastal:1;
};
struct TrainerMonItemCustomMoves
{
u16 iv;
u8 lvl;
u16 species;
u16 heldItem;
u16 moves[MAX_MON_MOVES];
};
#define NO_ITEM_DEFAULT_MOVES(party) { .NoItemDefaultMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = 0
#define NO_ITEM_CUSTOM_MOVES(party) { .NoItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET
#define ITEM_DEFAULT_MOVES(party) { .ItemDefaultMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_HELD_ITEM
#define ITEM_CUSTOM_MOVES(party) { .ItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM
union TrainerMonPtr
{
const struct TrainerMonNoItemDefaultMoves *NoItemDefaultMoves;
const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves;
const struct TrainerMonItemDefaultMoves *ItemDefaultMoves;
const struct TrainerMonItemCustomMoves *ItemCustomMoves;
};
#define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray)
struct Trainer
{
/*0x00*/ u8 partyFlags;
/*0x01*/ u8 trainerClass;
/*0x02*/ u8 encounterMusic_gender; // last bit is gender
/*0x03*/ u8 trainerPic;
/*0x04*/ u8 trainerName[12];
/*0x10*/ u16 items[MAX_TRAINER_ITEMS];
/*0x18*/ bool8 doubleBattle;
/*0x1C*/ u32 aiFlags;
/*0x00*/ u32 aiFlags;
/*0x04*/ const struct TrainerMon *party;
/*0x08*/ u16 items[MAX_TRAINER_ITEMS];
/*0x10*/ u8 trainerClass;
/*0x11*/ u8 encounterMusic_gender; // last bit is gender
/*0x12*/ u8 trainerPic;
/*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
/*0x1E*/ bool8 doubleBattle:1;
bool8 mugshotEnabled:1;
u8 startingStatus:6; // this trainer starts a battle with a given status. see include/constants/battle.h for values
/*0x1F*/ u8 mugshotColor;
/*0x20*/ u8 partySize;
/*0x24*/ const union TrainerMonPtr party;
};
// extern const u8 gSpeciesNames[][POKEMON_NAME_LENGTH + 1];
// extern const u8 gMoveNames[][MOVE_NAME_LENGTH + 1];
extern const u8 gTrainerClassNames[][13];
static inline u16 SanitizeTrainerId(u16 trainerId)
struct TrainerClass
{
if (trainerId >= NUM_TRAINERS)
return TRAINER_NONE;
return trainerId;
}
u8 name[13];
u8 money;
u16 ball;
};
struct TypeInfo
{
@ -116,12 +93,6 @@ struct TypeInfo
//u16 arceusForm;
};
// extern const struct MonCoords gMonFrontPicCoords[];
// extern const struct CompressedSpriteSheet gMonFrontPicTable[];
// extern const struct MonCoords gMonBackPicCoords[];
// extern const struct CompressedSpriteSheet gMonBackPicTable[];
// extern const struct CompressedSpritePalette gMonPaletteTable[];
// extern const struct CompressedSpritePalette gMonShinyPaletteTable[];
extern const union AnimCmd *const *const gTrainerFrontAnimsPtrTable[];
extern const struct MonCoords gTrainerFrontPicCoords[];
extern const struct CompressedSpriteSheet gTrainerFrontPicTable[];
@ -155,16 +126,85 @@ extern const struct SpriteFrameImage gTrainerBackPicTable_RSMay[];
extern const union AnimCmd sAnim_GeneralFrame0[];
extern const struct Trainer gTrainers[];
extern const struct Trainer gBattlePartners[];
extern const struct TrainerClass gTrainerClasses[TRAINER_CLASS_COUNT];
static inline u16 SanitizeTrainerId(u16 trainerId)
{
if (trainerId >= TRAINERS_COUNT)
return TRAINER_NONE;
return trainerId;
}
static inline const struct Trainer *GetTrainerStructFromId(u16 trainerId)
{
return &gTrainers[SanitizeTrainerId(trainerId)];
}
static inline const u8 GetTrainerClassFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].trainerClass;
}
static inline const u8 *GetTrainerClassNameFromId(u16 trainerId)
{
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
return gTrainerClasses[gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass].name;
return gTrainerClasses[GetTrainerClassFromId(trainerId)].name;
}
static inline const u8 *GetTrainerNameFromId(u16 trainerId)
{
// if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
// return gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
return gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
return gTrainers[SanitizeTrainerId(trainerId)].trainerName;
}
// static inline const struct TrainerMon *GetTrainerPartyFromId(u16 trainerId)
// {
// return gTrainers[SanitizeTrainerId(trainerId)].party;
// }
static inline const u8 GetTrainerPicFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].trainerPic;
}
static inline const u8 GetTrainerStartingStatusFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].startingStatus;
}
static inline const bool32 IsTrainerDoubleBattle(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].doubleBattle;
}
static inline const u8 GetTrainerPartySizeFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].partySize;
}
static inline const bool32 DoesTrainerHaveMugshot(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].mugshotEnabled;
}
static inline const u8 GetTrainerMugshotColorFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].mugshotColor;
}
static inline const u16 *GetTrainerItemsFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].items;
}
static inline const struct TrainerMon *GetTrainerPartyFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].party;
}
static inline const bool32 GetTrainerAIFlagsFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].aiFlags;
}
#endif // GUARD_DATA_H

View File

@ -745,11 +745,13 @@ 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);
u16 NationalPokedexNumToSpecies(u16 nationalNum);
u16 SpeciesToNationalPokedexNum(u16 species);
@ -777,7 +779,6 @@ u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves);
u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves);
u8 GetNumberOfRelearnableMoves(struct Pokemon *mon);
u16 SpeciesToPokedexNum(u16 species);
void ClearBattleMonForms(void);
void PlayBattleBGM(void);
void PlayMapChosenOrBattleBGM(u16 songId);
const u32 *GetMonFrontSpritePal(struct Pokemon *mon);
@ -819,5 +820,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

View File

@ -178,7 +178,7 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c
u16 RenderText(struct TextPrinter *textPrinter);
s32 (*GetFontWidthFunc(u8 glyphId))(u16, bool32);
s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing);
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str, int a3, int a4, int a5, int a6, int a7);
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str);
u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y);
u8 GetKeypadIconTileOffset(u8 keypadIconId);
u8 GetKeypadIconWidth(u8 keypadIconId);

View File

@ -34,6 +34,8 @@ struct WildPokemonHeader
};
extern const struct WildPokemonHeader gWildMonHeaders[];
extern bool8 gIsFishingEncounter;
extern bool8 gIsSurfingEncounter;
void DisableWildEncounters(bool8 disabled);
bool8 StandardWildEncounter(u32 currMetatileAttrs, u16 previousMetaTileBehavior);
@ -49,5 +51,6 @@ bool8 SweetScentWildEncounter(void);
void SeedWildEncounterRng(u16 randVal);
void ResetEncounterRateModifiers(void);
bool8 TryStandardWildEncounter(u32 currMetatileAttrs);
bool8 TryDoDoubleWildBattle(void);
#endif // GUARD_WILD_ENCOUNTER_H

View File

@ -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);

5189
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

File diff suppressed because it is too large Load Diff

View File

@ -2300,7 +2300,7 @@ void AnimTask_TransformMon(u8 taskId)
else
position = GetBattlerPosition(gBattleAnimAttacker);
src = gMonSpritesGfxPtr->sprites[position] + (gBattleMonForms[gBattleAnimAttacker] << 11);
src = gMonSpritesGfxPtr->sprites[position];
dest = animBg.bgTiles;
CpuCopy32(src, dest, MON_PIC_SIZE);
LoadBgTiles(1, animBg.bgTiles, 0x800, animBg.tilesOffset);

View File

@ -1,6 +1,7 @@
#include "global.h"
#include "gflib.h"
#include "battle_anim.h"
#include "battle_interface.h"
#include "data.h"
#include "decompress.h"
#include "pokemon_icon.h"
@ -86,10 +87,10 @@ u8 GetBattlerSpriteCoord(u8 battlerId, u8 coordType)
{
case BATTLER_COORD_X:
case BATTLER_COORD_X_2:
retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].x;
retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].x;
break;
case BATTLER_COORD_Y:
retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y;
retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y;
break;
case BATTLER_COORD_Y_PIC_OFFSET:
case BATTLER_COORD_Y_PIC_OFFSET_DEFAULT:
@ -208,7 +209,7 @@ static u8 GetBattlerSpriteFinal_Y(u8 battlerId, u16 species, bool8 a3)
offset = GetBattlerYDelta(battlerId, species);
offset -= GetBattlerElevation(battlerId, species);
}
y = offset + sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y;
y = offset + sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y;
if (a3)
{
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
@ -782,16 +783,6 @@ void InitSpritePosToAnimAttacker(struct Sprite *sprite, bool8 respectMonPicOffse
sprite->y += gBattleAnimArgs[1];
}
// u8 GetBattlerSide(u8 battlerId)
// {
// return GET_BATTLER_SIDE2(battlerId);
// }
// u8 GetBattlerPosition(u8 battlerId)
// {
// return GET_BATTLER_POSITION(battlerId);
// }
u8 GetBattlerAtPosition(u8 position)
{
u8 i;

View File

@ -2040,21 +2040,17 @@ void AnimTask_SetTargetToEffectBattler(u8 taskId)
void TryShinyAnimation(u8 battler, struct Pokemon *mon)
{
bool32 isShiny;
u32 otId, personality;
u32 shinyValue;
u8 taskId1, taskId2;
struct Pokemon* illusionMon;
isShiny = FALSE;
isShiny = GetMonData(mon, MON_DATA_IS_SHINY);
gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE;
otId = GetMonData(mon, MON_DATA_OT_ID);
personality = GetMonData(mon, MON_DATA_PERSONALITY);
illusionMon = GetIllusionMonPtr(battler);
if (illusionMon != NULL)
mon = illusionMon;
if (IsBattlerSpriteVisible(battler))
if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon))
{
shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
if (shinyValue < SHINY_ODDS)
isShiny = TRUE;
if (isShiny)
{
if (GetSpriteTileStartByTag(ANIM_TAG_GOLD_STARS) == 0xFFFF)

View File

@ -777,7 +777,7 @@ static void DrawLinkBattleParticipantPokeballs(u8 taskId, u8 multiplayerId, u8 b
}
else
{
if (multiplayerId == gBattleStruct->multiplayerId)
if (multiplayerId == gBattleScripting.multiplayerId)
pokeballStatuses = gTasks[taskId].data[3];
else
pokeballStatuses = gTasks[taskId].data[4];
@ -800,7 +800,7 @@ static void DrawLinkBattleVsScreenOutcomeText(void)
{
if (gBattleOutcome == B_OUTCOME_WON)
{
switch (gLinkPlayers[gBattleStruct->multiplayerId].id)
switch (gLinkPlayers[gBattleScripting.multiplayerId].id)
{
case 0:
BattlePutTextOnWindow(gText_Win, B_WIN_VS_OUTCOME_LEFT);
@ -822,7 +822,7 @@ static void DrawLinkBattleVsScreenOutcomeText(void)
}
else
{
switch (gLinkPlayers[gBattleStruct->multiplayerId].id)
switch (gLinkPlayers[gBattleScripting.multiplayerId].id)
{
case 0:
BattlePutTextOnWindow(gText_Win, B_WIN_VS_OUTCOME_RIGHT);
@ -845,7 +845,7 @@ static void DrawLinkBattleVsScreenOutcomeText(void)
}
else if (gBattleOutcome == B_OUTCOME_WON)
{
if (gLinkPlayers[gBattleStruct->multiplayerId].id != 0)
if (gLinkPlayers[gBattleScripting.multiplayerId].id != 0)
{
BattlePutTextOnWindow(gText_Win, B_WIN_VS_OUTCOME_RIGHT);
BattlePutTextOnWindow(gText_Loss, B_WIN_VS_OUTCOME_LEFT);
@ -858,7 +858,7 @@ static void DrawLinkBattleVsScreenOutcomeText(void)
}
else
{
if (gLinkPlayers[gBattleStruct->multiplayerId].id != 0)
if (gLinkPlayers[gBattleScripting.multiplayerId].id != 0)
{
BattlePutTextOnWindow(gText_Win, B_WIN_VS_OUTCOME_LEFT);
BattlePutTextOnWindow(gText_Loss, B_WIN_VS_OUTCOME_RIGHT);
@ -910,7 +910,7 @@ void InitLinkBattleVsScreen(u8 taskId)
}
else
{
u8 playerId = gBattleStruct->multiplayerId;
u8 playerId = gBattleScripting.multiplayerId;
u8 opponentId = playerId ^ BIT_SIDE;
u8 opponentId_copy = opponentId;

View File

@ -838,7 +838,7 @@ static void OakOldManHandleChoosePokemon(u32 battler)
gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF);
gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF;
*(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4;
*(&gBattleStruct->playerPartyIdx) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->prevSelectedPartySlot) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->abilityPreventingSwitchout) = (gBattleResources->bufferA[battler][3] & 0xFF) | (gBattleResources->bufferA[battler][7] << 8);
for (i = 0; i < 3; ++i)
gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][4 + i];

View File

@ -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);
@ -61,9 +66,9 @@ static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler)
[CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, // done
[CONTROLLER_CHOOSEACTION] = OpponentHandleChooseAction, // done
[CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done
[CONTROLLER_CHOOSEMOVE] = OpponentHandleChooseMove, // done TODO: AI refactoring
[CONTROLLER_CHOOSEMOVE] = OpponentHandleChooseMove, // done
[CONTROLLER_OPENBAG] = OpponentHandleChooseItem, // done
[CONTROLLER_CHOOSEPOKEMON] = OpponentHandleChoosePokemon, // done TODO: AI refactoring
[CONTROLLER_CHOOSEPOKEMON] = OpponentHandleChoosePokemon, // done
[CONTROLLER_23] = BtlController_Empty, // done
[CONTROLLER_HEALTHBARUPDATE] = OpponentHandleHealthBarUpdate, // done
[CONTROLLER_EXPUPDATE] = BtlController_Empty, // done
@ -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);

View File

@ -1002,37 +1002,38 @@ static void Intro_DelayAndEnd(u32 battler)
static void Intro_WaitForShinyAnimAndHealthbox(u32 battler)
{
bool8 var = FALSE;
bool8 healthboxAnimDone = FALSE;
if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))
{
if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy)
var = TRUE;
}
else
// Check if healthbox has finished sliding in
if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy
&& gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy)
var = TRUE;
healthboxAnimDone = TRUE;
}
if (IsCryPlayingOrClearCrySongs())
var = FALSE;
if (var && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim
else
{
if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy)
healthboxAnimDone = TRUE;
}
// If healthbox and shiny anim are done
if (healthboxAnimDone && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim
&& gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim)
{
// Reset shiny anim (even if it didn't occur)
gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE;
FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS);
FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS);
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
m4aMPlayContinue(&gMPlayInfo_BGM);
else
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256);
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler);
if (IsDoubleBattle())
if (TwoPlayerIntroMons(battler))
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], BATTLE_PARTNER(battler));
gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3;
gBattlerControllerFuncs[battler] = Intro_DelayAndEnd;
}
@ -1040,28 +1041,86 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler)
static void Intro_TryShinyAnimShowHealthbox(u32 battler)
{
if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive)
bool32 bgmRestored = FALSE;
bool32 battlerAnimsDone = FALSE;
// Start shiny animation if applicable for 1st Pokémon
if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim
&& !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive)
TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]);
// Start shiny animation if applicable for 2nd Pokémon
if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive)
TryShinyAnimation(BATTLE_PARTNER(battler), &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]);
// Show healthbox after ball anim
if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive)
{
if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim)
TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]);
if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim)
TryShinyAnimation(BATTLE_PARTNER(battler), &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]);
if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted)
{
DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]);
UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)],
&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]],
HEALTHBOX_ALL);
StartHealthboxSlideIn(BATTLE_PARTNER(battler));
SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]);
if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL);
StartHealthboxSlideIn(BATTLE_PARTNER(battler));
SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]);
}
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL);
StartHealthboxSlideIn(battler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]);
}
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = TRUE;
}
// Restore bgm after cry has played and healthbox anim is started
if (!gBattleSpritesDataPtr->healthBoxesData[battler].waitForCry
&& gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].waitForCry
&& !IsCryPlayingOrClearCrySongs())
{
if (!gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored)
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK)
m4aMPlayContinue(&gMPlayInfo_BGM);
else
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
}
gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = TRUE;
bgmRestored = TRUE;
}
// Wait for battler anims
if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy
&& gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy)
{
battlerAnimsDone = TRUE;
}
}
else
{
if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy)
{
battlerAnimsDone = TRUE;
}
}
// Clean up
if (bgmRestored && battlerAnimsDone)
{
if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]);
DestroySprite(&gSprites[gBattleControllerData[battler]]);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler],
&gPlayerParty[gBattlerPartyIndexes[battler]],
HEALTHBOX_ALL);
StartHealthboxSlideIn(battler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]);
gBattleSpritesDataPtr->animationData->introAnimActive = FALSE;
gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE;
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = FALSE;
gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox;
}
}
@ -1783,7 +1842,7 @@ static void PlayerHandleChoosePokemon(u32 battler)
gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF);
gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF;
*(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4;
*(&gBattleStruct->playerPartyIdx) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->prevSelectedPartySlot) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->abilityPreventingSwitchout) = (gBattleResources->bufferA[battler][3] & 0xFF) | (gBattleResources->bufferA[battler][7] << 8);
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gBattlerControllerFuncs[battler] = OpenPartyMenuToChooseMon;

View File

@ -735,7 +735,7 @@ static void PokedudeHandleChoosePokemon(u32 battler)
gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF);
gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF;
*(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4;
*(&gBattleStruct->playerPartyIdx) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->prevSelectedPartySlot) = gBattleResources->bufferA[battler][2];
*(&gBattleStruct->abilityPreventingSwitchout) = (gBattleResources->bufferA[battler][3] & 0xFF) | (gBattleResources->bufferA[battler][7] << 8);
for (i = 0; i < 3; ++i)
gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][4 + i];

View File

@ -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"
@ -69,8 +70,8 @@ void SetUpBattleVars(void)
HandleLinkBattleSetup();
gBattleControllerExecFlags = 0;
ClearBattleAnimationVars();
ClearBattleMonForms();
BattleAI_HandleItemUseBeforeAISetup();
BattleAI_SetupItems();
BattleAI_SetupFlags();
}
void InitBattleControllers(void)
@ -313,16 +314,13 @@ static void SetBattlePartyIds(void)
{
for (i = 0; i < gBattlersCount; i++)
{
for (j = 0; j < PARTY_SIZE; ++j)
for (j = 0; j < PARTY_SIZE; j++)
{
if (i < 2)
{
if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER)
if (GetBattlerSide(i) == B_SIDE_PLAYER)
{
if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0
&& GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
&& GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
&& !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG))
if (IsValidForBattle(&gPlayerParty[j]))
{
gBattlerPartyIndexes[i] = j;
break;
@ -330,10 +328,7 @@ static void SetBattlePartyIds(void)
}
else
{
if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0
&& GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
&& GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
&& !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG))
if (IsValidForBattle(&gEnemyParty[j]))
{
gBattlerPartyIndexes[i] = j;
break;
@ -342,13 +337,9 @@ static void SetBattlePartyIds(void)
}
else
{
if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER)
if (GetBattlerSide(i) == B_SIDE_PLAYER)
{
if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0
&& GetMonData(&gPlayerParty[j], MON_DATA_SPECIES) != SPECIES_NONE // Probably a typo by Game Freak. The rest use SPECIES2.
&& GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
&& !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG)
&& gBattlerPartyIndexes[i - 2] != j)
if (IsValidForBattle(&gPlayerParty[j]) && gBattlerPartyIndexes[i - 2] != j)
{
gBattlerPartyIndexes[i] = j;
break;
@ -356,19 +347,24 @@ static void SetBattlePartyIds(void)
}
else
{
if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0
&& GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
&& GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
&& !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG)
&& gBattlerPartyIndexes[i - 2] != j)
if (IsValidForBattle(&gEnemyParty[j]) && gBattlerPartyIndexes[i - 2] != j)
{
gBattlerPartyIndexes[i] = j;
break;
}
}
// No valid mons were found. Add the empty slot.
if (gBattlerPartyIndexes[i - 2] == 0)
gBattlerPartyIndexes[i] = 1;
else
gBattlerPartyIndexes[i] = 0;
}
}
}
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
gBattlerPartyIndexes[1] = 0, gBattlerPartyIndexes[3] = 3;
}
}
@ -837,7 +833,7 @@ void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, u16 it
}
// Unused
static void BtlController_EmitUnknownYesNoBox(u32 battler, u32 bufferId, u32 arg1) // TODO: Does the function name make sense for pokefirered?
static void BtlController_EmitUnknownYesNoBox(u32 battler, u32 bufferId, u32 arg1)
{
gBattleResources->transferBuffer[0] = CONTROLLER_UNKNOWNYESNOBOX;
gBattleResources->transferBuffer[1] = arg1;

View File

@ -139,11 +139,7 @@ void SpriteCB_WaitForBattlerBallReleaseAnim(struct Sprite *sprite)
if (gSprites[spriteId].animPaused)
gSprites[spriteId].animPaused = 0;
else if (gSprites[spriteId].animEnded)
{
gSprites[spriteId].callback = SetIdleSpriteCallback;
StartSpriteAffineAnim(&gSprites[spriteId], 0);
sprite->callback = SpriteCallbackDummy;
}
}
// Unused
@ -211,7 +207,6 @@ bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 de
if (tableId == B_ANIM_FORM_CHANGE && (argument & 0x80))
{
gBattleMonForms[activeBattler] = (argument & ~(0x80));
return TRUE;
}
else if (gBattleSpritesDataPtr->battlerData[activeBattler].behindSubstitute
@ -313,6 +308,8 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
u32 monsPersonality, currentPersonality, isShiny, species, paletteOffset, position;
const void *lzPaletteData;
struct Pokemon *illusionMon = GetIllusionMonPtr(battler);
if (illusionMon != NULL)
mon = illusionMon;
if (GetMonData(mon, MON_DATA_IS_EGG) || GetMonData(mon, MON_DATA_SPECIES) == SPECIES_NONE) // Don't load GFX of egg pokemon.
return;
@ -497,7 +494,12 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
else
{
if (state == 2)
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]);
{
if (WhichBattleCoords(0))
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]);
else
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesPlayerHealthbox);
}
else if (state == 3)
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[1]);
else if (state == 4)
@ -697,7 +699,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16));
gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies;
}
gBattleMonForms[battlerAtk] = gBattleMonForms[battlerDef];
gSprites[gBattlerSpriteIds[battlerAtk]].y = GetBattlerSpriteDefault_Y(battlerAtk);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], 0);
}
@ -717,11 +719,7 @@ void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite)
LZDecompressVram(gSubstituteDollTilemap, gMonSpritesGfxPtr->sprites[position]);
for (i = 1; i < 4; ++i)
{
u8 (*ptr)[4][0x800] = gMonSpritesGfxPtr->sprites[position];
++ptr;
--ptr;
DmaCopy32Defvars(3, (*ptr)[0], (*ptr)[i], 0x800);
Dma3CopyLarge32_(gMonSpritesGfxPtr->sprites[position], &gMonSpritesGfxPtr->sprites[position][MON_PIC_SIZE * i], MON_PIC_SIZE);
}
palOffset = OBJ_PLTT_ID(battlerId);
LoadCompressedPalette(gSubstituteDollPal, palOffset, PLTT_SIZE_4BPP);
@ -738,7 +736,7 @@ void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite)
void LoadBattleMonGfxAndAnimate(u8 battlerId, bool8 loadMonSprite, u8 spriteId)
{
BattleLoadSubstituteOrMonSpriteGfx(battlerId, loadMonSprite);
StartSpriteAnim(&gSprites[spriteId], gBattleMonForms[battlerId]);
StartSpriteAnim(&gSprites[spriteId], 0);
if (!loadMonSprite)
gSprites[spriteId].y = GetSubstituteSpriteDefault_Y(battlerId);
else
@ -946,7 +944,6 @@ void BattleInterfaceSetWindowPals(void)
void ClearTemporarySpeciesSpriteData(u8 battlerId, bool8 dontClearSubstitute)
{
gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies = SPECIES_NONE;
gBattleMonForms[battlerId] = 0;
if (!dontClearSubstitute)
ClearBehindSubstituteBit(battlerId);
}

View File

@ -67,7 +67,7 @@ struct TestingBar
static void SpriteCB_HealthBoxOther(struct Sprite *sprite);
static void SpriteCB_HealthBar(struct Sprite *sprite);
static const u8 *GetBattleInterfaceGfxPtr(u8 which);
static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent);
static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp);
static void Task_HidePartyStatusSummary_BattleStart_1(u8 taskId);
static void Task_HidePartyStatusSummary_BattleStart_2(u8 taskId);
static void SpriteCB_PartySummaryBar_Exit(struct Sprite *sprite);
@ -682,7 +682,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
u8 healthbarSpriteId;
struct Sprite *healthbarSprite;
if (!IsDoubleBattle())
if (WhichBattleCoords(battlerId) == 0)
{
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
{
@ -864,7 +864,7 @@ void InitBattlerHealthboxCoords(u8 battler)
{
s16 x = 0, y = 0;
if (!IsDoubleBattle())
if (!WhichBattleCoords(battler))
{
if (GetBattlerSide(battler) != B_SIDE_PLAYER)
x = 44, y = 30;
@ -910,7 +910,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER)
{
objVram = (void *)(OBJ_VRAM0);
if (!IsDoubleBattle())
if (!WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId))
objVram += spriteTileNum + 0x820;
else
objVram += spriteTileNum + 0x420;
@ -929,7 +929,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp
u32 windowId, spriteTileNum;
u8 *windowTileData;
if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER && !IsDoubleBattle())
if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER && !WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId))
{
// Only in the Japanese release can HP be displayed as text outside of double battles
u8 text[8];
@ -960,12 +960,12 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp
u8 text[20] = __("{COLOR 01}{HIGHLIGHT 02}");
battler = gSprites[healthboxSpriteId].sBattlerId;
if (IsDoubleBattle() == TRUE || GetBattlerSide(battler) == B_SIDE_OPPONENT)
if (WhichBattleCoords(battler) == TRUE || GetBattlerSide(battler) == B_SIDE_OPPONENT)
{
if (maxOrCurrent != HP_CURRENT)
UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxHp, maxOrCurrent);
UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxOrCurrent, currHp, maxHp);
else
UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, currHp, maxOrCurrent);
UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxOrCurrent, currHp, maxHp);
}
else
{
@ -992,7 +992,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp
ConvertIntToDecimalStringN(text + 6, maxHp, STR_CONV_MODE_RIGHT_ALIGN, 3);
else
ConvertIntToDecimalStringN(text + 6, currHp, STR_CONV_MODE_RIGHT_ALIGN, 3);
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0);
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text);
for (i = 0; i < 3; i++)
{
@ -1006,7 +1006,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp
static const u8 sText_Slash[] = _("/");
static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent)
static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp)
{
u32 windowId, spriteTileNum;
u8 *windowTileData;
@ -1027,11 +1027,15 @@ static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8
var = 0;
healthBarSpriteId = gSprites[healthboxSpriteId].sHealthBarSpriteId;
txtPtr = ConvertIntToDecimalStringN(text + 6, value, STR_CONV_MODE_RIGHT_ALIGN, 3);
if (maxOrCurrent == HP_CURRENT)
txtPtr = ConvertIntToDecimalStringN(text + 6, currHp, STR_CONV_MODE_RIGHT_ALIGN, 3);
else
txtPtr = ConvertIntToDecimalStringN(text + 6, maxHp, STR_CONV_MODE_RIGHT_ALIGN, 3);
if (maxOrCurrent == HP_CURRENT)
StringCopy(txtPtr, sText_Slash);
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0);
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text);
for (i = var; i < var + 3; i++)
{
@ -1082,7 +1086,7 @@ static void PrintSafariMonInfo(u8 healthboxSpriteId, struct Pokemon *mon)
var = 5;
nature = GetNature(mon);
StringCopy(text + 6, gNatureNamePointers[nature]);
RenderTextHandleBold(barFontGfx, 0, text, 0, 0, 0, 0, 0);
RenderTextHandleBold(barFontGfx, 0, text);
for (j = 6, i = 0; i < var; i++, j++)
{
@ -1114,7 +1118,7 @@ static void PrintSafariMonInfo(u8 healthboxSpriteId, struct Pokemon *mon)
ConvertIntToDecimalStringN(text + 9, gBattleStruct->safariEscapeFactor, STR_CONV_MODE_RIGHT_ALIGN, 2);
text[5] = CHAR_SPACE;
text[8] = CHAR_SLASH;
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0);
RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text);
j = healthBarSpriteId; // Needed to match for some reason.
for (j = 0; j < 5; j++)
@ -1143,7 +1147,7 @@ void SwapHpBarsWithHpText(void)
{
if (gSprites[gHealthboxSpriteIds[i]].callback == SpriteCallbackDummy
&& GetBattlerSide(i) != B_SIDE_OPPONENT
&& (IsDoubleBattle() || GetBattlerSide(i) != B_SIDE_PLAYER))
&& (WhichBattleCoords(i) || GetBattlerSide(i) != B_SIDE_PLAYER))
{
bool8 noBars;
@ -1151,18 +1155,20 @@ void SwapHpBarsWithHpText(void)
noBars = gBattleSpritesDataPtr->battlerData[i].hpNumbersNoBars;
if (GetBattlerSide(i) == B_SIDE_PLAYER)
{
if (!IsDoubleBattle())
if (!WhichBattleCoords(i))
continue;
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
continue;
if (noBars == TRUE) // bars to text
{
s16 currHp = GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_HP);
s16 maxHp = GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP);
healthBarSpriteId = gSprites[gHealthboxSpriteIds[i]].sHealthBarSpriteId;
CpuFill32(0, (void *)(OBJ_VRAM0 + gSprites[healthBarSpriteId].oam.tileNum * TILE_SIZE_4BPP), 8 * TILE_SIZE_4BPP);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_HP), HP_CURRENT);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP), HP_MAX);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_CURRENT, currHp, maxHp);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_MAX, currHp, maxHp);
}
else // text to bars
{
@ -1184,11 +1190,13 @@ void SwapHpBarsWithHpText(void)
}
else
{
s16 currHp = GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_HP);
s16 maxHp = GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP);
healthBarSpriteId = gSprites[gHealthboxSpriteIds[i]].sHealthBarSpriteId;
CpuFill32(0, (void *)(OBJ_VRAM0 + gSprites[healthBarSpriteId].oam.tileNum * 32), 8 * TILE_SIZE_4BPP);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_HP), HP_CURRENT);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP), HP_MAX);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_CURRENT, currHp, maxHp);
UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_MAX, currHp, maxHp);
}
}
else // text to bars
@ -1496,7 +1504,7 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo,
{
isOpponent = TRUE;
if (!isSwitchingMons || !IsDoubleBattle())
if (!isSwitchingMons || !WhichBattleCoords(battlerId))
x = 104, y = 40;
else
x = 104, y = 16;
@ -1935,7 +1943,7 @@ void UpdateNickInHealthbox(u8 healthboxSpriteId, struct Pokemon *mon)
{
TextIntoHealthboxObject((void *)(OBJ_VRAM0 + 0x40 + spriteTileNum), windowTileData, 6);
ptr = (void *)(OBJ_VRAM0);
if (!IsDoubleBattle())
if (!WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId))
ptr += spriteTileNum + 0x800;
else
ptr += spriteTileNum + 0x400;
@ -2005,7 +2013,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId)
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
{
status = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_STATUS);
if (!IsDoubleBattle())
if (!WhichBattleCoords(battlerId))
tileNumAdder = 0x1A;
else
tileNumAdder = 0x12;
@ -2065,7 +2073,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId)
FillPalette(sStatusIconColors[statusPalId], pltAdder + OBJ_PLTT_OFFSET, PLTT_SIZEOF(1));
CpuCopy16(&gPlttBufferUnfaded[OBJ_PLTT_OFFSET + pltAdder], (u16 *)OBJ_PLTT + pltAdder, PLTT_SIZEOF(1));
CpuCopy32(statusGfxPtr, (void *)(OBJ_VRAM0 + (gSprites[healthboxSpriteId].oam.tileNum + tileNumAdder) * TILE_SIZE_4BPP), 3 * TILE_SIZE_4BPP);
if (IsDoubleBattle() == TRUE || GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
if (WhichBattleCoords(battlerId) == TRUE || GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
{
if (!gBattleSpritesDataPtr->battlerData[battlerId].hpNumbersNoBars)
{
@ -2171,31 +2179,30 @@ static void UpdateLeftNoOfBallsTextOnHealthbox(u8 healthboxSpriteId)
void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elementId)
{
s32 maxHp, currHp;
u8 battlerId = gSprites[healthboxSpriteId].sBattlerId;
s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP);
s32 currHp = GetMonData(mon, MON_DATA_HP);
if (elementId == HEALTHBOX_ALL && !IsDoubleBattle())
GetBattlerSide(battlerId); // Pointless function call.
if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER)
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
{
u8 isDoubles;
u8 isDoubles = WhichBattleCoords(battlerId);
maxHp = GetMonData(mon, MON_DATA_MAX_HP);
currHp = GetMonData(mon, MON_DATA_HP);
if (elementId == HEALTHBOX_LEVEL || elementId == HEALTHBOX_ALL)
UpdateLvlInHealthbox(healthboxSpriteId, GetMonData(mon, MON_DATA_LEVEL));
if (elementId == HEALTHBOX_CURRENT_HP || elementId == HEALTHBOX_ALL)
UpdateHpTextInHealthbox(healthboxSpriteId, HP_CURRENT, currHp, maxHp);
if (elementId == HEALTHBOX_MAX_HP || elementId == HEALTHBOX_ALL)
UpdateHpTextInHealthbox(healthboxSpriteId, HP_MAX, currHp, maxHp);
if (elementId == HEALTHBOX_HEALTH_BAR || elementId == HEALTHBOX_ALL)
{
LoadBattleBarGfx(0);
SetBattleBarStruct(battlerId, healthboxSpriteId, maxHp, currHp, 0);
MoveBattleBar(battlerId, healthboxSpriteId, HEALTH_BAR, 0);
}
isDoubles = IsDoubleBattle();
if (!isDoubles && (elementId == HEALTHBOX_EXP_BAR || elementId == HEALTHBOX_ALL))
{
u16 species;

View File

@ -471,7 +471,7 @@ void CopyBattlerSpriteToBg(s32 bgId, u8 x, u8 y, u8 battlerPosition, u8 palno, u
u8 battler = GetBattlerAtPosition(battlerPosition);
s32 offset = tilesOffset;
CpuCopy16(gMonSpritesGfxPtr->sprites[battlerPosition] + BG_SCREEN_SIZE * gBattleMonForms[battler], tilesDest, BG_SCREEN_SIZE);
CpuCopy16(gMonSpritesGfxPtr->sprites[battlerPosition], tilesDest, BG_SCREEN_SIZE);
LoadBgTiles(bgId, tilesDest, 0x1000, tilesOffset);
for (i = y; i < y + 8; ++i)
for (j = x; j < x + 8; ++j)

File diff suppressed because it is too large Load Diff

View File

@ -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[] =
{
@ -2478,7 +2478,7 @@ void BufferStringBattle(u32 battler, u16 stringId)
}
else if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY)
stringPtr = sText_WildPkmnAppeared2;
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) // interesting, looks like they had something planned for wild double battles
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]])) // interesting, looks like they had something planned for wild double battles
stringPtr = sText_TwoWildPkmnAppeared;
else if (gBattleTypeFlags & BATTLE_TYPE_OLD_MAN_TUTORIAL)
stringPtr = sText_WildPkmnAppearedPause;
@ -2489,9 +2489,13 @@ void BufferStringBattle(u32 battler, u16 stringId)
case STRINGID_INTROSENDOUT: // poke first send-out
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]))
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
stringPtr = sText_InGamePartnerSentOutZGoN;
else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
stringPtr = sText_GoTwoPkmn;
else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
stringPtr = sText_LinkPartnerSentOutPkmnGoPkmn;
else
stringPtr = sText_GoTwoPkmn;
@ -2503,9 +2507,13 @@ void BufferStringBattle(u32 battler, u16 stringId)
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]))
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
if (BATTLE_TWO_VS_ONE_OPPONENT)
stringPtr = sText_Trainer1SentOutTwoPkmn;
else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
stringPtr = sText_TwoTrainersSentPkmn;
else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
stringPtr = sText_TwoLinkTrainersSentOutPkmn;
else if (gBattleTypeFlags & BATTLE_TYPE_LINK)
stringPtr = sText_LinkTrainerSentOutTwoPkmn;
@ -2827,6 +2835,24 @@ static const u8 *BattleStringGetTrainerName(u8 *text, u8 multiplayerId, u8 battl
return BattleStringGetOpponentName(text, multiplayerId, battler);
}
static const u8 *BattleStringGetOpponentClassByTrainerId(u16 trainerId)
{
const u8 *toCpy;
if (trainerId == TRAINER_UNION_ROOM)
toCpy = gTrainerClasses[GetUnionRoomTrainerClass()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
toCpy = gTrainerClasses[GetBattleTowerTrainerClassNameId()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER)
toCpy = gTrainerClasses[GetTrainerTowerOpponentClass()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER)
toCpy = gTrainerClasses[GetEreaderTrainerClassId()].name;
else
toCpy = gTrainerClasses[GetTrainerClassFromId(trainerId)].name;
return toCpy;
}
u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
{
u32 dstId = 0; // if they used dstId, why not use srcId as well?
@ -2876,18 +2902,14 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
toCpy = gBattleTextBuff3;
break;
case B_TXT_TRAINER2_CLASS:
// TODO: trainer name
// toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_B);
toCpy = "TODO";
toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_B);
break;
case B_TXT_TRAINER2_NAME:
// TODO: trainer name
// toCpy = BattleStringGetOpponentNameByTrainerId(gTrainerBattleOpponent_B, text, multiplayerId, GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT));
toCpy = "TODO";
toCpy = BattleStringGetOpponentNameByTrainerId(gTrainerBattleOpponent_B, text, multiplayerId, GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT));
break;
case B_TXT_PARTNER_CLASS:
// TODO: trainer name
// toCpy = gTrainerClassNames[GetFrontierOpponentClass(gPartnerTrainerId)].name;
// toCpy = gTrainerClasses[GetFrontierOpponentClass(gPartnerTrainerId)].name;
toCpy = "TODO";
break;
case B_TXT_PARTNER_NAME:
@ -3000,8 +3022,8 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
if ((gBattleStruct->multiplayerId != 0 && (gPotentialItemEffectBattler & BIT_SIDE))
|| (gBattleStruct->multiplayerId == 0 && !(gPotentialItemEffectBattler & BIT_SIDE)))
if ((gBattleScripting.multiplayerId != 0 && (gPotentialItemEffectBattler & BIT_SIDE))
|| (gBattleScripting.multiplayerId == 0 && !(gPotentialItemEffectBattler & BIT_SIDE)))
{
StringCopy(text, gEnigmaBerries[gPotentialItemEffectBattler].name);
StringAppend(text, sText_BerrySuffix);
@ -3014,7 +3036,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
}
else
{
if (gLinkPlayers[gBattleStruct->multiplayerId].id == gPotentialItemEffectBattler)
if (gLinkPlayers[gBattleScripting.multiplayerId].id == gPotentialItemEffectBattler)
{
StringCopy(text, gEnigmaBerries[gPotentialItemEffectBattler].name);
StringAppend(text, sText_BerrySuffix);
@ -3053,17 +3075,17 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
break;
case B_TXT_TRAINER1_CLASS: // trainer class name
if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
toCpy = gTrainerClassNames[GetSecretBaseTrainerNameIndex()];
toCpy = gTrainerClasses[GetSecretBaseTrainerNameIndex()].name;
else if (gTrainerBattleOpponent_A == TRAINER_UNION_ROOM)
toCpy = gTrainerClassNames[GetUnionRoomTrainerClass()];
toCpy = gTrainerClasses[GetUnionRoomTrainerClass()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
toCpy = gTrainerClassNames[GetBattleTowerTrainerClassNameId()];
toCpy = gTrainerClasses[GetBattleTowerTrainerClassNameId()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER)
toCpy = gTrainerClassNames[GetTrainerTowerOpponentClass()];
toCpy = gTrainerClasses[GetTrainerTowerOpponentClass()].name;
else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER)
toCpy = gTrainerClassNames[GetEreaderTrainerClassId()];
toCpy = gTrainerClasses[GetEreaderTrainerClassId()].name;
else
toCpy = gTrainerClassNames[gTrainers[gTrainerBattleOpponent_A].trainerClass];
toCpy = gTrainerClasses[gTrainers[gTrainerBattleOpponent_A].trainerClass].name;
break;
case B_TXT_TRAINER1_NAME: // trainer1 name
if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
@ -3199,24 +3221,24 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
toCpy = BattleStringGetTrainerName(text, multiplayerId, gBattlerAttacker);
break;
case B_TXT_ATK_TRAINER_CLASS:
// TODO: get trainer name
toCpy = "TODO";
// switch (GetBattlerPosition(gBattlerAttacker))
// {
// case B_POSITION_PLAYER_RIGHT:
// if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
// toCpy = gTrainerClasses[GetFrontierOpponentClass(gPartnerTrainerId)].name;
// break;
// case B_POSITION_OPPONENT_LEFT:
// toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_A);
// break;
// case B_POSITION_OPPONENT_RIGHT:
// if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT)
// toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_B);
// else
// toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_A);
// break;
// }
switch (GetBattlerPosition(gBattlerAttacker))
{
case B_POSITION_PLAYER_RIGHT:
// TODO: implement partner trainers?
// if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
// toCpy = gTrainerClasses[GetFrontierOpponentClass(gPartnerTrainerId)].name;
toCpy = gTrainerClasses[TRAINER_CLASS_NONE].name;
break;
case B_POSITION_OPPONENT_LEFT:
toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_A);
break;
case B_POSITION_OPPONENT_RIGHT:
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT)
toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_B);
else
toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_A);
break;
}
break;
case B_TXT_ATK_TEAM1:
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
@ -3364,7 +3386,7 @@ static void ExpandBattleTextBuffPlaceholders(const u8 *src, u8 *dst)
{
if (hword == ITEM_ENIGMA_BERRY)
{
if (gLinkPlayers[gBattleStruct->multiplayerId].id == gPotentialItemEffectBattler)
if (gLinkPlayers[gBattleScripting.multiplayerId].id == gPotentialItemEffectBattler)
{
StringCopy(dst, gEnigmaBerries[gPotentialItemEffectBattler].name);
StringAppend(dst, sText_BerrySuffix);

View File

@ -20,14 +20,16 @@
#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"
#include "battle_interface.h"
#include "rtc.h"
#include "wild_encounter.h"
#include "constants/battle_anim.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_script_commands.h"
@ -321,7 +323,6 @@ static const u16 sWhiteOutBadgeMoney[9] = { 8, 16, 24, 36, 48, 64, 80, 100, 120
#define TAG_LVLUP_BANNER_MON_ICON 55130
static void TrySetDestinyBondToHappen(void);
static u8 AttacksThisTurn(u8 battlerId, u16 move); // Note: returns 1 if it's a charging turn, otherwise 2.
static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr);
static void InitLevelUpBanner(void);
static bool8 SlideInLevelUpBanner(void);
@ -1400,14 +1401,13 @@ static void Cmd_attackcanceler(void)
}
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
// TODO: Z-Moves and Dynamax
// if ((gBattleStruct->zmove.active || IsMaxMove(gCurrentMove))
// && IS_BATTLER_PROTECTED(gBattlerTarget))
// {
// BattleScriptPush(cmd->nextInstr);
// gBattlescriptCurrInstr = BattleScript_CouldntFullyProtect;
// return;
// }
if ((gBattleStruct->zmove.active || IsMaxMove(gCurrentMove))
&& IS_BATTLER_PROTECTED(gBattlerTarget))
{
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_CouldntFullyProtect;
return;
}
for (i = 0; i < gBattlersCount; i++)
{
@ -1935,6 +1935,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();
@ -1973,19 +1981,6 @@ static void Cmd_damagecalc(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void AI_CalcDmg(u8 attacker, u8 defender)
{
u32 sideStatus = gSideStatuses[GET_BATTLER_SIDE(defender)];
u8 moveType;
s32 critChance;
GET_MOVE_TYPE(gCurrentMove, moveType);
critChance = CalcCritChanceStage(attacker, defender, gCurrentMove, TRUE);
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1);
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, attacker, defender, moveType, 0, gIsCriticalHit, TRUE, TRUE);
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE);
}
static void Cmd_typecalc(void)
{
CMD_ARGS();
@ -1998,127 +1993,6 @@ static void Cmd_typecalc(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Same as ModulateDmgByType except different arguments
static void ModulateDmgByType2(uq4_12_t multiplier, u16 move, u8 *flags)
{
gBattleMoveDamage = uq4_12_multiply(gBattleMoveDamage, multiplier);
if (gBattleMoveDamage == 0 && multiplier != 0)
gBattleMoveDamage = 1;
switch (multiplier)
{
case UQ_4_12(0.0):
*flags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
*flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
*flags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
break;
case UQ_4_12(0.5):
if (gMovesInfo[move].power && !(*flags & MOVE_RESULT_NO_EFFECT))
{
if (*flags & MOVE_RESULT_SUPER_EFFECTIVE)
*flags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
else
*flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE;
}
break;
case UQ_4_12(2.0):
if (gMovesInfo[move].power && !(*flags & MOVE_RESULT_NO_EFFECT))
{
if (*flags & MOVE_RESULT_NOT_VERY_EFFECTIVE)
*flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
else
*flags |= MOVE_RESULT_SUPER_EFFECTIVE;
}
break;
}
}
u8 TypeCalc(u16 move, u8 attacker, u8 defender)
{
s32 i = 0;
u8 flags = 0;
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gMovesInfo[move].type;
// check stab
if (IS_BATTLER_OF_TYPE(attacker, moveType))
{
gBattleMoveDamage = gBattleMoveDamage * 15;
gBattleMoveDamage = gBattleMoveDamage / 10;
}
if (gBattleMons[defender].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
}
else
{
ModulateDmgByType2(gTypeEffectivenessTable[moveType][gBattleMons[defender].type1], move, &flags);
ModulateDmgByType2(gTypeEffectivenessTable[moveType][gBattleMons[defender].type2], move, &flags);
}
if (gBattleMons[defender].ability == ABILITY_WONDER_GUARD && !(flags & MOVE_RESULT_MISSED)
&& AttacksThisTurn(attacker, move) == 2
&& (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)))
&& gMovesInfo[move].power)
{
flags |= MOVE_RESULT_MISSED;
}
return flags;
}
u8 AI_TypeCalc(u16 move, u16 targetSpecies, u16 targetAbility)
{
s32 i = 0;
u8 flags = 0;
u8 type1 = gSpeciesInfo[targetSpecies].types[0], type2 = gSpeciesInfo[targetSpecies].types[1];
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gMovesInfo[move].type;
if (targetAbility == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags = MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE;
}
else
{
ModulateDmgByType2(gTypeEffectivenessTable[moveType][type1], move, &flags);
ModulateDmgByType2(gTypeEffectivenessTable[moveType][type2], move, &flags);
}
if (targetAbility == ABILITY_WONDER_GUARD
&& (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)))
&& gMovesInfo[move].power)
flags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
return flags;
}
// Multiplies the damage by a random factor between 85% to 100% inclusive
static inline void ApplyRandomDmgMultiplier(void)
{
u16 rand = Random();
u16 randPercent = 100 - (rand % 16);
if (gBattleMoveDamage != 0)
{
gBattleMoveDamage *= randPercent;
gBattleMoveDamage /= 100;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
}
}
static void Unused_ApplyRandomDmgMultiplier(void)
{
ApplyRandomDmgMultiplier();
}
static void Cmd_adjustdamage(void)
{
CMD_ARGS();
@ -6499,7 +6373,7 @@ static void Cmd_getswitchedmondata(void)
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (gBattleControllerExecFlags)
return;
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
BtlController_EmitGetMonData(battler, BUFFER_A, REQUEST_ALL_BATTLE, gBitTable[gBattlerPartyIndexes[battler]]);
MarkBattlerForControllerExec(battler);
@ -7654,14 +7528,11 @@ static u32 GetTrainerMoneyToGive(u16 trainerId)
}
else
{
// TODO: Update Trainer struct
// const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
// if (party == NULL)
// return 20;
// lastMonLevel = party[GetTrainerPartySizeFromId(trainerId) - 1].lvl;
// trainerMoney = gTrainerClasses[GetTrainerClassFromId(trainerId)].money;
lastMonLevel = 10;
trainerMoney = 100;
const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
if (party == NULL)
return 20;
lastMonLevel = party[GetTrainerPartySizeFromId(trainerId) - 1].lvl;
trainerMoney = gTrainerClasses[GetTrainerClassFromId(trainerId)].money;
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * trainerMoney;
@ -13072,24 +12943,6 @@ static void Cmd_copymovepermanently(void)
}
}
static u8 AttacksThisTurn(u8 battlerId, u16 move) // Note: returns 1 if it's a charging turn, otherwise 2
{
// first argument is unused
if (gMovesInfo[move].effect == EFFECT_SOLAR_BEAM
&& (gBattleWeather & B_WEATHER_SUN))
return 2;
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK
|| gMovesInfo[move].effect == EFFECT_SOLAR_BEAM
|| gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE
|| gMovesInfo[move].effect == EFFECT_BIDE)
{
if ((gHitMarker & HITMARKER_CHARGING))
return 1;
}
return 2;
}
static void Cmd_trychoosesleeptalkmove(void)
{
CMD_ARGS(const u8 *failInstr);
@ -15171,10 +15024,9 @@ static void Cmd_handleballthrow(void)
ballMultiplier = B_NET_BALL_MODIFIER >= GEN_7 ? 350 : 300;
break;
case ITEM_DIVE_BALL:
// TODO: gIsFishingEncounter and gIsSurfingEncounter
// if (GetCurrentMapType() == MAP_TYPE_UNDERWATER
// || (B_DIVE_BALL_MODIFIER >= GEN_4 && (gIsFishingEncounter || gIsSurfingEncounter)))
// ballMultiplier = 350;
if (GetCurrentMapType() == MAP_TYPE_UNDERWATER
|| (B_DIVE_BALL_MODIFIER >= GEN_4 && (gIsFishingEncounter || gIsSurfingEncounter)))
ballMultiplier = 350;
break;
case ITEM_NEST_BALL:
if (B_NEST_BALL_MODIFIER >= GEN_6)
@ -15224,16 +15076,15 @@ static void Cmd_handleballthrow(void)
ballMultiplier = 200;
break;
case ITEM_LURE_BALL:
// TODO: gIsFishingEncounter
// if (gIsFishingEncounter)
// {
// if (B_LURE_BALL_MODIFIER >= GEN_8)
// ballMultiplier = 400;
// else if (B_LURE_BALL_MODIFIER >= GEN_7)
// ballMultiplier = 500;
// else
// ballMultiplier = 300;
// }
if (gIsFishingEncounter)
{
if (B_LURE_BALL_MODIFIER >= GEN_8)
ballMultiplier = 400;
else if (B_LURE_BALL_MODIFIER >= GEN_7)
ballMultiplier = 500;
else
ballMultiplier = 300;
}
break;
case ITEM_MOON_BALL:
{

View File

@ -59,7 +59,7 @@ struct TrainerBattleParameter
static void DoSafariBattle(void);
static void DoGhostBattle(void);
static void DoStandardWildBattle(void);
static void DoStandardWildBattle(bool32 isDouble);
static void CB2_EndWildBattle(void);
static u8 GetWildBattleTransition(void);
static u8 GetTrainerBattleTransition(void);
@ -242,16 +242,23 @@ void StartWildBattle(void)
else if (CheckSilphScopeInPokemonTower(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum))
DoGhostBattle();
else
DoStandardWildBattle();
DoStandardWildBattle(FALSE);
}
static void DoStandardWildBattle(void)
void StartDoubleWildBattle(void)
{
DoStandardWildBattle(TRUE);
}
static void DoStandardWildBattle(bool32 isDouble)
{
LockPlayerFieldControls();
FreezeObjectEvents();
StopPlayerAvatar();
gMain.savedCallback = CB2_EndWildBattle;
gBattleTypeFlags = 0;
if (isDouble)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
CreateBattleStartTask(GetWildBattleTransition(), 0);
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
IncrementGameStat(GAME_STAT_WILD_BATTLES);
@ -564,49 +571,17 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons)
u8 i;
u8 sum;
u32 count = numMons;
const struct TrainerMon *party;
if (GetTrainerPartySizeFromId(opponentId) < count)
count = GetTrainerPartySizeFromId(opponentId);
if (gTrainers[opponentId].partySize < count)
count = gTrainers[opponentId].partySize;
sum = 0;
switch (gTrainers[opponentId].partyFlags)
{
case 0:
{
const struct TrainerMonNoItemDefaultMoves *party;
party = gTrainers[opponentId].party.NoItemDefaultMoves;
for (i = 0; i < count; ++i)
sum += party[i].lvl;
}
break;
case F_TRAINER_PARTY_CUSTOM_MOVESET:
{
const struct TrainerMonNoItemCustomMoves *party;
party = GetTrainerPartyFromId(opponentId);
for (i = 0; i < count && party != NULL; i++)
sum += party[i].lvl;
party = gTrainers[opponentId].party.NoItemCustomMoves;
for (i = 0; i < count; ++i)
sum += party[i].lvl;
}
break;
case F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemDefaultMoves *party;
party = gTrainers[opponentId].party.ItemDefaultMoves;
for (i = 0; i < count; ++i)
sum += party[i].lvl;
}
break;
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemCustomMoves *party;
party = gTrainers[opponentId].party.ItemCustomMoves;
for (i = 0; i < count; ++i)
sum += party[i].lvl;
}
break;
}
return sum;
}
@ -628,29 +603,30 @@ static u8 GetTrainerBattleTransition(void)
u8 transitionType;
u8 enemyLevel;
u8 playerLevel;
u32 trainerId = SanitizeTrainerId(gTrainerBattleOpponent_A);
if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
if (trainerId == TRAINER_SECRET_BASE)
return B_TRANSITION_BLUE;
if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_ELITE_FOUR)
if (gTrainers[trainerId].trainerClass == TRAINER_CLASS_ELITE_FOUR)
{
if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LORELEI || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LORELEI_2)
if (trainerId == TRAINER_ELITE_FOUR_LORELEI || trainerId == TRAINER_ELITE_FOUR_LORELEI_2)
return B_TRANSITION_LORELEI;
if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_BRUNO || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_BRUNO_2)
if (trainerId == TRAINER_ELITE_FOUR_BRUNO || trainerId == TRAINER_ELITE_FOUR_BRUNO_2)
return B_TRANSITION_BRUNO;
if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_AGATHA || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_AGATHA_2)
if (trainerId == TRAINER_ELITE_FOUR_AGATHA || trainerId == TRAINER_ELITE_FOUR_AGATHA_2)
return B_TRANSITION_AGATHA;
if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LANCE || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LANCE_2)
if (trainerId == TRAINER_ELITE_FOUR_LANCE || trainerId == TRAINER_ELITE_FOUR_LANCE_2)
return B_TRANSITION_LANCE;
return B_TRANSITION_BLUE;
}
if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_CHAMPION)
if (gTrainers[trainerId].trainerClass == TRAINER_CLASS_CHAMPION)
return B_TRANSITION_BLUE;
if (gTrainers[gTrainerBattleOpponent_A].doubleBattle == TRUE)
if (gTrainers[trainerId].doubleBattle == TRUE)
minPartyCount = 2; // double battles always at least have 2 pokemon.
else
minPartyCount = 1;
transitionType = GetBattleTransitionTypeByMap();
enemyLevel = GetSumOfEnemyPartyLevel(gTrainerBattleOpponent_A, minPartyCount);
enemyLevel = GetSumOfEnemyPartyLevel(trainerId, minPartyCount);
playerLevel = GetSumOfPlayerPartyLevel(minPartyCount);
if (enemyLevel < playerLevel)
return sBattleTransitionTable_Trainer[transitionType][0];

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
#include "battle.h"
#include "battle_anim.h"
#include "malloc.h"
#include "party_menu.h"
#include "pokemon.h"
#include "trainer_tower.h"
@ -20,6 +21,10 @@ void AllocateBattleResources(void)
gBattleStruct = AllocZeroed(sizeof(*gBattleStruct));
#if B_FLAG_SKY_BATTLE
gBattleStruct->isSkyBattle = FlagGet(B_FLAG_SKY_BATTLE);
#endif
gBattleResources = AllocZeroed(sizeof(*gBattleResources));
gBattleResources->secretBase = AllocZeroed(sizeof(*gBattleResources->secretBase));
gBattleResources->flags = AllocZeroed(sizeof(*gBattleResources->flags));
@ -27,8 +32,9 @@ void AllocateBattleResources(void)
gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack));
gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp));
gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai));
gBattleResources->aiData = AllocZeroed(sizeof(*gBattleResources->aiData));
gBattleResources->aiParty = AllocZeroed(sizeof(*gBattleResources->aiParty));
gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory));
gBattleResources->AI_ScriptsStack = AllocZeroed(sizeof(*gBattleResources->AI_ScriptsStack));
gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
gLinkBattleRecvBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
@ -53,6 +59,7 @@ void FreeBattleResources(void)
FREE_AND_SET_NULL(gPokedudeBattlerStates[i]);
}
}
gFieldStatuses = 0;
if (gBattleResources != NULL)
{
FREE_AND_SET_NULL(gBattleStruct);
@ -63,8 +70,9 @@ void FreeBattleResources(void)
FREE_AND_SET_NULL(gBattleResources->battleCallbackStack);
FREE_AND_SET_NULL(gBattleResources->beforeLvlUp);
FREE_AND_SET_NULL(gBattleResources->ai);
FREE_AND_SET_NULL(gBattleResources->aiData);
FREE_AND_SET_NULL(gBattleResources->aiParty);
FREE_AND_SET_NULL(gBattleResources->battleHistory);
FREE_AND_SET_NULL(gBattleResources->AI_ScriptsStack);
FREE_AND_SET_NULL(gBattleResources);
FREE_AND_SET_NULL(gLinkBattleSendBuffer);
@ -106,3 +114,18 @@ void AdjustFriendshipOnBattleFaint(u8 battler)
AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battler]], FRIENDSHIP_EVENT_FAINT_SMALL);
}
}
void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1)
{
if (GetBattlerSide(battler) != B_SIDE_OPPONENT)
{
s32 i;
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = *(0 * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders));
SwitchPartyMonSlots(GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battler]), GetPartyIdFromBattlePartyId(arg1));
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
*(0 * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)) = gBattlePartyCurrentOrder[i];
}
}

View File

@ -1004,12 +1004,14 @@ const struct Berry * GetBerryInfo(u8 berryIdx)
return &gBerries[berryIdx - 1];
}
u8 ItemIdToBerryType(u16 itemId)
u8 ItemIdToBerryType(u16 item)
{
if (itemId - FIRST_BERRY_INDEX < 0 || itemId - FIRST_BERRY_INDEX > ITEM_ENIGMA_BERRY - FIRST_BERRY_INDEX)
return 1;
u16 berry = item - FIRST_BERRY_INDEX;
return ITEM_TO_BERRY(itemId);
if (berry > LAST_BERRY_INDEX - FIRST_BERRY_INDEX)
return ITEM_TO_BERRY(FIRST_BERRY_INDEX);
else
return ITEM_TO_BERRY(item);
}
u16 BerryTypeToItemId(u16 berryType)

View File

@ -3,6 +3,7 @@
#include "battle.h"
#include "data.h"
#include "graphics.h"
#include "constants/abilities.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/battle_ai.h"
@ -290,5 +291,5 @@ const union AnimCmd *const gAnims_MonPic[] =
#include "data/trainer_graphics/back_pic_tables.h"
#include "data/trainer_parties.h"
#include "data/text/trainer_class_names.h"
#include "data/trainers.h"
#include "data/battle_partners.h"

View File

@ -2,7 +2,7 @@ const struct Trainer gBattlePartners[] = {
[PARTNER_NONE] =
{
.party = NULL,
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
.trainerClass = TRAINER_CLASS_NONE,
.encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE,
.trainerPic = TRAINER_PIC_HIKER,
.trainerName = _(""),

View File

@ -1,109 +0,0 @@
const u8 gTrainerClassNames[][13] = {
[TRAINER_CLASS_NONE] = _("{PKMN} TRAINER"),
[TRAINER_CLASS_PKMN_TRAINER_UNUSED] = _("{PKMN} TRAINER"),
[TRAINER_CLASS_AQUA_LEADER] = _("AQUA LEADER"),
[TRAINER_CLASS_TEAM_AQUA] = _("TEAM AQUA"),
[TRAINER_CLASS_RS_AROMA_LADY] = _("AROMA LADY"),
[TRAINER_CLASS_RS_RUIN_MANIAC] = _("RUIN MANIAC"),
[TRAINER_CLASS_INTERVIEWER] = _("INTERVIEWER"),
[TRAINER_CLASS_RS_TUBER_F] = _("TUBER"),
[TRAINER_CLASS_RS_TUBER_M] = _("TUBER"),
[TRAINER_CLASS_RS_COOLTRAINER] = _("COOLTRAINER"),
[TRAINER_CLASS_HEX_MANIAC] = _("HEX MANIAC"),
[TRAINER_CLASS_RS_LADY] = _("LADY"),
[TRAINER_CLASS_RS_BEAUTY] = _("BEAUTY"),
[TRAINER_CLASS_RICH_BOY] = _("RICH BOY"),
[TRAINER_CLASS_RS_POKEMANIAC] = _("POKéMANIAC"),
[TRAINER_CLASS_RS_SWIMMER_M] = _("SWIMMER♂"),
[TRAINER_CLASS_RS_BLACK_BELT] = _("BLACK BELT"),
[TRAINER_CLASS_GUITARIST] = _("GUITARIST"),
[TRAINER_CLASS_KINDLER] = _("KINDLER"),
[TRAINER_CLASS_RS_CAMPER] = _("CAMPER"),
[TRAINER_CLASS_BUG_MANIAC] = _("BUG MANIAC"),
[TRAINER_CLASS_RS_PSYCHIC] = _("PSYCHIC"),
[TRAINER_CLASS_RS_GENTLEMAN] = _("GENTLEMAN"),
[TRAINER_CLASS_RS_ELITE_FOUR] = _("ELITE FOUR"),
[TRAINER_CLASS_RS_LEADER] = _("LEADER"),
[TRAINER_CLASS_SCHOOL_KID] = _("SCHOOL KID"),
[TRAINER_CLASS_SR_AND_JR] = _("SR. AND JR."),
[TRAINER_CLASS_POKEFAN] = _("POKéFAN"),
[TRAINER_CLASS_EXPERT] = _("EXPERT"),
[TRAINER_CLASS_RS_YOUNGSTER] = _("YOUNGSTER"),
[TRAINER_CLASS_RS_CHAMPION] = _("CHAMPION"),
[TRAINER_CLASS_RS_FISHERMAN] = _("FISHERMAN"),
[TRAINER_CLASS_TRIATHLETE] = _("TRIATHLETE"),
[TRAINER_CLASS_DRAGON_TAMER] = _("DRAGON TAMER"),
[TRAINER_CLASS_RS_BIRD_KEEPER] = _("BIRD KEEPER"),
[TRAINER_CLASS_NINJA_BOY] = _("NINJA BOY"),
[TRAINER_CLASS_BATTLE_GIRL] = _("BATTLE GIRL"),
[TRAINER_CLASS_PARASOL_LADY] = _("PARASOL LADY"),
[TRAINER_CLASS_RS_SWIMMER_F] = _("SWIMMER♀"),
[TRAINER_CLASS_RS_PICNICKER] = _("PICNICKER"),
[TRAINER_CLASS_RS_TWINS] = _("TWINS"),
[TRAINER_CLASS_RS_SAILOR] = _("SAILOR"),
[TRAINER_CLASS_BOARDER] = _("BOARDER"),
[TRAINER_CLASS_COLLECTOR] = _("COLLECTOR"),
[TRAINER_CLASS_PKMN_TRAINER] = _("{PKMN} TRAINER"),
[TRAINER_CLASS_RS_PKMN_BREEDER] = _("{PKMN} BREEDER"),
[TRAINER_CLASS_RS_PKMN_RANGER] = _("{PKMN} RANGER"),
[TRAINER_CLASS_MAGMA_LEADER] = _("MAGMA LEADER"),
[TRAINER_CLASS_TEAM_MAGMA] = _("TEAM MAGMA"),
[TRAINER_CLASS_RS_LASS] = _("LASS"),
[TRAINER_CLASS_RS_BUG_CATCHER] = _("BUG CATCHER"),
[TRAINER_CLASS_RS_HIKER] = _("HIKER"),
[TRAINER_CLASS_RS_YOUNG_COUPLE] = _("YOUNG COUPLE"),
[TRAINER_CLASS_OLD_COUPLE] = _("OLD COUPLE"),
[TRAINER_CLASS_RS_SIS_AND_BRO] = _("SIS AND BRO"),
[TRAINER_CLASS_AQUA_ADMIN] = _("AQUA ADMIN"),
[TRAINER_CLASS_MAGMA_ADMIN] = _("MAGMA ADMIN"),
[TRAINER_CLASS_YOUNGSTER] = _("YOUNGSTER"),
[TRAINER_CLASS_BUG_CATCHER] = _("BUG CATCHER"),
[TRAINER_CLASS_LASS] = _("LASS"),
[TRAINER_CLASS_SAILOR] = _("SAILOR"),
[TRAINER_CLASS_CAMPER] = _("CAMPER"),
[TRAINER_CLASS_PICNICKER] = _("PICNICKER"),
[TRAINER_CLASS_POKEMANIAC] = _("POKéMANIAC"),
[TRAINER_CLASS_SUPER_NERD] = _("SUPER NERD"),
[TRAINER_CLASS_HIKER] = _("HIKER"),
[TRAINER_CLASS_BIKER] = _("BIKER"),
[TRAINER_CLASS_BURGLAR] = _("BURGLAR"),
[TRAINER_CLASS_ENGINEER] = _("ENGINEER"),
[TRAINER_CLASS_FISHERMAN] = _("FISHERMAN"),
[TRAINER_CLASS_SWIMMER_M] = _("SWIMMER♂"),
[TRAINER_CLASS_CUE_BALL] = _("CUE BALL"),
[TRAINER_CLASS_GAMER] = _("GAMER"),
[TRAINER_CLASS_BEAUTY] = _("BEAUTY"),
[TRAINER_CLASS_SWIMMER_F] = _("SWIMMER♀"),
[TRAINER_CLASS_PSYCHIC] = _("PSYCHIC"),
[TRAINER_CLASS_ROCKER] = _("ROCKER"),
[TRAINER_CLASS_JUGGLER] = _("JUGGLER"),
[TRAINER_CLASS_TAMER] = _("TAMER"),
[TRAINER_CLASS_BIRD_KEEPER] = _("BIRD KEEPER"),
[TRAINER_CLASS_BLACK_BELT] = _("BLACK BELT"),
[TRAINER_CLASS_RIVAL_EARLY] = _("RIVAL"),
[TRAINER_CLASS_SCIENTIST] = _("SCIENTIST"),
[TRAINER_CLASS_BOSS] = _("BOSS"),
[TRAINER_CLASS_LEADER] = _("LEADER"),
[TRAINER_CLASS_TEAM_ROCKET] = _("TEAM ROCKET"),
[TRAINER_CLASS_COOLTRAINER] = _("COOLTRAINER"),
[TRAINER_CLASS_ELITE_FOUR] = _("ELITE FOUR"),
[TRAINER_CLASS_GENTLEMAN] = _("GENTLEMAN"),
[TRAINER_CLASS_RIVAL_LATE] = _("RIVAL"),
[TRAINER_CLASS_CHAMPION] = _("CHAMPION"),
[TRAINER_CLASS_CHANNELER] = _("CHANNELER"),
[TRAINER_CLASS_TWINS] = _("TWINS"),
[TRAINER_CLASS_COOL_COUPLE] = _("COOL COUPLE"),
[TRAINER_CLASS_YOUNG_COUPLE] = _("YOUNG COUPLE"),
[TRAINER_CLASS_CRUSH_KIN] = _("CRUSH KIN"),
[TRAINER_CLASS_SIS_AND_BRO] = _("SIS AND BRO"),
[TRAINER_CLASS_PKMN_PROF] = _("{PKMN} PROF."),
[TRAINER_CLASS_PLAYER] = _("PLAYER"),
[TRAINER_CLASS_CRUSH_GIRL] = _("CRUSH GIRL"),
[TRAINER_CLASS_TUBER] = _("TUBER"),
[TRAINER_CLASS_PKMN_BREEDER] = _("{PKMN} BREEDER"),
[TRAINER_CLASS_PKMN_RANGER] = _("{PKMN} RANGER"),
[TRAINER_CLASS_AROMA_LADY] = _("AROMA LADY"),
[TRAINER_CLASS_RUIN_MANIAC] = _("RUIN MANIAC"),
[TRAINER_CLASS_LADY] = _("LADY"),
[TRAINER_CLASS_PAINTER] = _("PAINTER"),
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8263,27 +8263,27 @@
{
"min_level": 3,
"max_level": 3,
"species": "SPECIES_CHARIZARD"
"species": "SPECIES_FARFETCHD_GALARIAN"
},
{
"min_level": 3,
"max_level": 3,
"species": "SPECIES_CHARIZARD"
"species": "SPECIES_FARFETCHD_GALARIAN"
},
{
"min_level": 3,
"max_level": 3,
"species": "SPECIES_CHARIZARD"
"species": "SPECIES_FARFETCHD_GALARIAN"
},
{
"min_level": 3,
"max_level": 3,
"species": "SPECIES_CHARIZARD"
"species": "SPECIES_FARFETCHD_GALARIAN"
},
{
"min_level": 2,
"max_level": 2,
"species": "SPECIES_CHARIZARD"
"species": "SPECIES_FARFETCHD_GALARIAN"
},
{
"min_level": 2,
@ -10499,29 +10499,29 @@
"encounter_rate": 21,
"mons": [
{
"min_level": 6,
"max_level": 6,
"species": "SPECIES_PARAS"
"min_level": 14,
"max_level": 14,
"species": "SPECIES_MAGIKARP"
},
{
"min_level": 6,
"max_level": 6,
"species": "SPECIES_PARAS"
"min_level": 14,
"max_level": 14,
"species": "SPECIES_MAGIKARP"
},
{
"min_level": 6,
"max_level": 6,
"species": "SPECIES_PARAS"
"min_level": 14,
"max_level": 14,
"species": "SPECIES_MAGIKARP"
},
{
"min_level": 6,
"max_level": 6,
"species": "SPECIES_PARAS"
"min_level": 14,
"max_level": 14,
"species": "SPECIES_MAGIKARP"
},
{
"min_level": 2,
"max_level": 2,
"species": "SPECIES_RATTATA"
"min_level": 14,
"max_level": 14,
"species": "SPECIES_MAGIKARP"
},
{
"min_level": 2,

View File

@ -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:
@ -6307,10 +6325,10 @@ void ChooseMonForWirelessMinigame(void)
static u8 GetPartyLayoutFromBattleType(void)
{
if (IsDoubleBattle() == FALSE)
return PARTY_LAYOUT_SINGLE;
if (IsMultiBattle() == TRUE)
return PARTY_LAYOUT_MULTI;
if (!IsDoubleBattle() || gPlayerPartyCount == 1)
return PARTY_LAYOUT_SINGLE;
return PARTY_LAYOUT_DOUBLE;
}
@ -6431,7 +6449,7 @@ static bool8 TrySwitchInPokemon(void)
StringExpandPlaceholders(gStringVar4, gText_EggCantBattle);
return FALSE;
}
if (GetPartyIdFromBattleSlot(slot) == gBattleStruct->playerPartyIdx)
if (GetPartyIdFromBattleSlot(slot) == gBattleStruct->prevSelectedPartySlot)
{
GetMonNickname(&gPlayerParty[slot], gStringVar1);
StringExpandPlaceholders(gStringVar4, gText_PkmnAlreadySelected);
@ -6560,12 +6578,12 @@ static void BufferBattlePartyOrderBySide(u8 *partyBattleOrder, u8 flankId, u8 ba
{
j = 1;
partyIndexes[0] = gBattlerPartyIndexes[leftBattler];
for (i = 0; i < PARTY_SIZE; ++i)
for (i = 0; i < PARTY_SIZE; i++)
{
if (i != partyIndexes[0])
{
partyIndexes[j] = i;
++j;
j++;
}
}
}
@ -6574,16 +6592,16 @@ static void BufferBattlePartyOrderBySide(u8 *partyBattleOrder, u8 flankId, u8 ba
j = 2;
partyIndexes[0] = gBattlerPartyIndexes[leftBattler];
partyIndexes[1] = gBattlerPartyIndexes[rightBattler];
for (i = 0; i < PARTY_SIZE; ++i)
for (i = 0; i < PARTY_SIZE; i++)
{
if (i != partyIndexes[0] && i != partyIndexes[1])
{
partyIndexes[j] = i;
++j;
j++;
}
}
}
for (i = 0; i < 3; ++i)
for (i = 0; i < 3; i++)
partyBattleOrder[i] = (partyIndexes[0 + (i * 2)] << 4) | partyIndexes[1 + (i * 2)];
}

View File

@ -672,8 +672,10 @@ static void SpriteCB_BallThrow_Shake(struct Sprite *sprite)
#define tCryTaskSpecies data[0]
#define tCryTaskPan data[1]
#define tCryTaskWantedCry data[2]
#define tCryTaskMonPtr1 data[3]
#define tCryTaskMonPtr2 data[4]
#define tCryTaskBattler data[3]
#define tCryTaskMonSpriteId data[4]
#define tCryTaskMonPtr1 data[5]
#define tCryTaskMonPtr2 data[6]
#define tCryTaskFrames data[10]
#define tCryTaskState data[15]
@ -682,15 +684,15 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
u8 wantedCry = gTasks[taskId].tCryTaskWantedCry;
s8 pan = gTasks[taskId].tCryTaskPan;
u16 species = gTasks[taskId].tCryTaskSpecies;
u8 battlerId = gTasks[taskId].tCryTaskBattler;
u8 monSpriteId = gTasks[taskId].tCryTaskMonSpriteId;
struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
switch (gTasks[taskId].tCryTaskState)
{
case 0:
default:
if (gTasks[taskId].data[8] < 3)
gTasks[taskId].data[8]++;
else
if (gSprites[monSpriteId].affineAnimEnded)
gTasks[taskId].tCryTaskState = wantedCry + 1;
break;
case 1:
@ -699,7 +701,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
PlayCry_ByMode(species, pan, CRY_MODE_NORMAL);
else
PlayCry_ByMode(species, pan, CRY_MODE_WEAK);
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
DestroyTask(taskId);
break;
case 2:
@ -716,6 +718,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
else
PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK_DOUBLES);
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
DestroyTask(taskId);
}
else
@ -755,6 +758,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
else
PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK);
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE;
DestroyTask(taskId);
break;
}
@ -773,7 +777,7 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite)
if (gMain.inBattle)
{
struct Pokemon *mon;
struct Pokemon *mon, *illusionMon;
u16 species;
s8 pan;
u16 wantedCryCase;
@ -812,10 +816,20 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite)
else
wantedCryCase = 2;
gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = TRUE;
taskId = CreateTask(Task_PlayCryWhenReleasedFromBall, 3);
gTasks[taskId].tCryTaskSpecies = species;
illusionMon = GetIllusionMonPtr(battlerId);
if (illusionMon != NULL)
gTasks[taskId].tCryTaskSpecies = GetMonData(illusionMon, MON_DATA_SPECIES);
else
gTasks[taskId].tCryTaskSpecies = GetMonData(mon, MON_DATA_SPECIES);
gTasks[taskId].tCryTaskPan = pan;
gTasks[taskId].tCryTaskWantedCry = wantedCryCase;
gTasks[taskId].tCryTaskBattler = battlerId;
gTasks[taskId].tCryTaskMonSpriteId = gBattlerSpriteIds[sprite->sBattler];
gTasks[taskId].tCryTaskMonPtr1 = (u32)(mon) >> 16;
gTasks[taskId].tCryTaskMonPtr2 = (u32)(mon);
gTasks[taskId].tCryTaskState = 0;
@ -829,6 +843,8 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite)
#undef tCryTaskSpecies
#undef tCryTaskPan
#undef tCryTaskWantedCry
#undef tCryTaskBattler
#undef tCryTaskMonSpriteId
#undef tCryTaskMonPtr1
#undef tCryTaskMonPtr2
#undef tCryTaskFrames

View File

@ -62,7 +62,6 @@ static EWRAM_DATA struct MonSpritesGfxManager *sMonSpritesGfxManager = NULL;
static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType);
static bool8 IsShinyOtIdPersonality(u32 otId, u32 personality);
static u8 GetNatureFromPersonality(u32 personality);
static bool8 PartyMonHasStatus(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId);
static bool8 IsPokemonStorageFull(void);
static u8 CopyMonToPC(struct Pokemon *mon);
@ -3126,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;
}
@ -3370,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);
@ -3982,7 +4028,7 @@ u8 GetNature(struct Pokemon *mon)
return GetMonData(mon, MON_DATA_PERSONALITY, NULL) % NUM_NATURES;
}
static u8 GetNatureFromPersonality(u32 personality)
u8 GetNatureFromPersonality(u32 personality)
{
return personality % NUM_NATURES;
}
@ -5053,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;
@ -5186,13 +5317,6 @@ u16 SpeciesToPokedexNum(u16 species)
return species > 0 ? species : 0xFFFF;
}
void ClearBattleMonForms(void)
{
int i;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
gBattleMonForms[i] = 0;
}
static u16 GetBattleBGM(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON)
@ -5590,9 +5714,9 @@ u16 FacilityClassToPicIndex(u16 facilityClass)
u16 PlayerGenderToFrontTrainerPicId(u8 playerGender)
{
if (playerGender != MALE)
return FacilityClassToPicIndex(FACILITY_CLASS_MAY);
return FacilityClassToPicIndex(FACILITY_CLASS_LEAF);
else
return FacilityClassToPicIndex(FACILITY_CLASS_BRENDAN);
return FacilityClassToPicIndex(FACILITY_CLASS_RED);
}
void HandleSetPokedexFlag(u16 nationalNum, u8 caseId, u32 personality)

View File

@ -128,7 +128,7 @@ void TrySetQuestLogLinkBattleEvent(void)
}
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
data->playerNames[0][i] = gLinkPlayers[gBattleStruct->multiplayerId ^ 1].name[i];
data->playerNames[0][i] = gLinkPlayers[gBattleScripting.multiplayerId ^ 1].name[i];
}
SetQuestLogEvent(eventId, (const u16 *)data);
Free(data);
@ -139,12 +139,12 @@ static void GetLinkMultiBattlePlayerIndexes(s32 * partnerIdx, s32 * opponentIdxs
{
s32 i;
s32 numOpponentsFound = 0;
u8 partnerId = gLinkPlayers[gBattleStruct->multiplayerId].id ^ 2;
u8 partnerId = gLinkPlayers[gBattleScripting.multiplayerId].id ^ 2;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (partnerId == gLinkPlayers[i].id)
*partnerIdx = i;
else if (i != gBattleStruct->multiplayerId)
else if (i != gBattleScripting.multiplayerId)
opponentIdxs[numOpponentsFound++] = i;
}
}

View File

@ -236,7 +236,7 @@ static void CreateBattlerSprite(u8 battler)
gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0);
}
else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT)
{
@ -270,7 +270,7 @@ static void CreateBattlerSprite(u8 battler)
gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0);
}
gSprites[gBattlerSpriteIds[battler]].invisible = gBattleSpritesDataPtr->battlerData[battler].invisible;
}
@ -306,9 +306,10 @@ static void CreateHealthboxSprite(u8 battler)
if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0)
SetHealthboxSpriteInvisible(healthboxSpriteId);
}
else if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0)
{
SetHealthboxSpriteInvisible(healthboxSpriteId);
else if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI))
{
if (!IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[battler]]))
SetHealthboxSpriteInvisible(healthboxSpriteId);
}
}
}

View File

@ -1179,7 +1179,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
return width;
}
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str, int a3, int a4, int a5, int a6, int a7)
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str)
{
u8 shadowColor;
u8 *strLocal;

View File

@ -4280,7 +4280,7 @@ static void ViewURoomPartnerTrainerCard(u8 *unused, struct WirelessLink_URoom *
DynamicPlaceholderTextUtil_Reset();
StringCopy(uroom->trainerCardStrBuffer[0], gTrainerClassNames[GetUnionRoomTrainerClass()]);
StringCopy(uroom->trainerCardStrBuffer[0], gTrainerClasses[GetUnionRoomTrainerClass()].name);
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, uroom->trainerCardStrBuffer[0]);
DynamicPlaceholderTextUtil_SetPlaceholderPtr(1, trainerCard->rse.playerName);

View File

@ -13,6 +13,7 @@
#include "script.h"
#include "link.h"
#include "quest_log.h"
#include "safari_zone.h"
#include "constants/maps.h"
#include "constants/abilities.h"
#include "constants/items.h"
@ -33,6 +34,8 @@ struct WildEncounterData
static EWRAM_DATA struct WildEncounterData sWildEncounterData = {};
static EWRAM_DATA bool8 sWildEncountersDisabled = FALSE;
EWRAM_DATA bool8 gIsFishingEncounter = 0;
EWRAM_DATA bool8 gIsSurfingEncounter = 0;
static bool8 UnlockedTanobyOrAreNotInTanoby(void);
static u32 GenerateUnownPersonalityByLetter(u8 letter);
@ -392,7 +395,17 @@ bool8 StandardWildEncounter(u32 currMetatileAttrs, u16 previousMetatileBehavior)
// try a regular wild land encounter
if (TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_REPEL) == TRUE)
{
StartWildBattle();
if (TryDoDoubleWildBattle())
{
struct Pokemon mon1 = gEnemyParty[0];
TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE);
gEnemyParty[1] = mon1;
StartDoubleWildBattle();
}
else
{
StartWildBattle();
}
return TRUE;
}
else
@ -428,8 +441,19 @@ bool8 StandardWildEncounter(u32 currMetatileAttrs, u16 previousMetatileBehavior)
else // try a regular surfing encounter
{
if (TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_REPEL) == TRUE)
{
StartWildBattle();
{
gIsSurfingEncounter = TRUE;
if (TryDoDoubleWildBattle())
{
struct Pokemon mon1 = gEnemyParty[0];
TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_KEEN_EYE);
gEnemyParty[1] = mon1;
StartDoubleWildBattle();
}
else
{
StartWildBattle();
}
return TRUE;
}
else
@ -520,6 +544,7 @@ void FishingWildEncounter(u8 rod)
{
GenerateFishingEncounter(gWildMonHeaders[GetCurrentMapWildMonHeaderId()].fishingMonsInfo, rod);
IncrementGameStat(GAME_STAT_FISHING_CAPTURES);
gIsFishingEncounter = TRUE;
StartWildBattle();
}
@ -782,3 +807,15 @@ static void AddToWildEncounterRateBuff(u8 encounterRate)
else
sWildEncounterData.encounterRateBuff = 0;
}
bool8 TryDoDoubleWildBattle(void)
{
if (GetSafariZoneFlag()
|| (B_DOUBLE_WILD_REQUIRE_2_MONS == TRUE && GetMonsStateToDoubles() != PLAYER_HAS_TWO_USABLE_MONS))
return FALSE;
else if (B_FLAG_FORCE_DOUBLE_WILD != 0 && FlagGet(B_FLAG_FORCE_DOUBLE_WILD))
return TRUE;
else if (B_DOUBLE_WILD_CHANCE != 0 && ((Random() % 100) + 1 <= B_DOUBLE_WILD_CHANCE))
return TRUE;
return FALSE;
}

View File

@ -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"