diff --git a/charmap.txt b/charmap.txt index 1c0367e7c..71e7a3aec 100644 --- a/charmap.txt +++ b/charmap.txt @@ -45,6 +45,7 @@ SUPER_ER = 2C LV = 34 '=' = 35 ';' = 36 +V_D_ARROW = 38 '¿' = 51 '¡' = 52 PK = 53 diff --git a/graphics/types/battle_icons1.pal b/graphics/types/battle_icons1.pal new file mode 100644 index 000000000..972dfd02a --- /dev/null +++ b/graphics/types/battle_icons1.pal @@ -0,0 +1,15 @@ +JASC-PAL +0100 +12 +120 152 128 +49 49 49 +255 255 255 +164 82 57 +172 189 32 +184 160 90 +213 180 90 +98 98 180 +180 90 164 +172 164 148 +172 172 197 +152 172 246 diff --git a/graphics/types/battle_icons1.png b/graphics/types/battle_icons1.png new file mode 100644 index 000000000..c47b07f11 Binary files /dev/null and b/graphics/types/battle_icons1.png differ diff --git a/graphics/types/battle_icons2.pal b/graphics/types/battle_icons2.pal new file mode 100644 index 000000000..bddf49ad9 --- /dev/null +++ b/graphics/types/battle_icons2.pal @@ -0,0 +1,15 @@ +JASC-PAL +0100 +12 +120 152 128 +49 49 49 +255 255 255 +240 82 48 +120 200 80 +248 197 49 +80 120 136 +123 98 230 +248 115 164 +57 156 255 +90 205 230 +246 180 246 diff --git a/graphics/types/battle_icons2.png b/graphics/types/battle_icons2.png new file mode 100644 index 000000000..44aa4f895 Binary files /dev/null and b/graphics/types/battle_icons2.png differ diff --git a/graphics/types/bug.png b/graphics/types/bug.png new file mode 100644 index 000000000..2f59a4838 Binary files /dev/null and b/graphics/types/bug.png differ diff --git a/graphics/types/contest_beauty.png b/graphics/types/contest_beauty.png new file mode 100644 index 000000000..b52fe3ca4 Binary files /dev/null and b/graphics/types/contest_beauty.png differ diff --git a/graphics/types/contest_cool.png b/graphics/types/contest_cool.png new file mode 100644 index 000000000..f0ca72323 Binary files /dev/null and b/graphics/types/contest_cool.png differ diff --git a/graphics/types/contest_cute.png b/graphics/types/contest_cute.png new file mode 100644 index 000000000..eeb86c431 Binary files /dev/null and b/graphics/types/contest_cute.png differ diff --git a/graphics/types/contest_smart.png b/graphics/types/contest_smart.png new file mode 100644 index 000000000..7659cc4f2 Binary files /dev/null and b/graphics/types/contest_smart.png differ diff --git a/graphics/types/contest_tough.png b/graphics/types/contest_tough.png new file mode 100644 index 000000000..f928596af Binary files /dev/null and b/graphics/types/contest_tough.png differ diff --git a/graphics/types/dark.png b/graphics/types/dark.png new file mode 100644 index 000000000..3380297da Binary files /dev/null and b/graphics/types/dark.png differ diff --git a/graphics/types/dragon.png b/graphics/types/dragon.png new file mode 100644 index 000000000..12430ad06 Binary files /dev/null and b/graphics/types/dragon.png differ diff --git a/graphics/types/electric.png b/graphics/types/electric.png new file mode 100644 index 000000000..0be6b890b Binary files /dev/null and b/graphics/types/electric.png differ diff --git a/graphics/types/fairy.png b/graphics/types/fairy.png new file mode 100644 index 000000000..498386c20 Binary files /dev/null and b/graphics/types/fairy.png differ diff --git a/graphics/types/fight.png b/graphics/types/fight.png new file mode 100644 index 000000000..95ff5a928 Binary files /dev/null and b/graphics/types/fight.png differ diff --git a/graphics/types/fire.png b/graphics/types/fire.png new file mode 100644 index 000000000..f6db950c8 Binary files /dev/null and b/graphics/types/fire.png differ diff --git a/graphics/types/flying.png b/graphics/types/flying.png new file mode 100644 index 000000000..2f34851ca Binary files /dev/null and b/graphics/types/flying.png differ diff --git a/graphics/types/ghost.png b/graphics/types/ghost.png new file mode 100644 index 000000000..b7347014d Binary files /dev/null and b/graphics/types/ghost.png differ diff --git a/graphics/types/grass.png b/graphics/types/grass.png new file mode 100644 index 000000000..4432a52e6 Binary files /dev/null and b/graphics/types/grass.png differ diff --git a/graphics/types/ground.png b/graphics/types/ground.png new file mode 100644 index 000000000..d3f6787b2 Binary files /dev/null and b/graphics/types/ground.png differ diff --git a/graphics/types/ice.png b/graphics/types/ice.png new file mode 100644 index 000000000..5ff2f69aa Binary files /dev/null and b/graphics/types/ice.png differ diff --git a/graphics/types/move_types_1.pal b/graphics/types/move_types_1.pal new file mode 100644 index 000000000..951aa69cb --- /dev/null +++ b/graphics/types/move_types_1.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 213 49 +246 131 49 +197 49 41 +230 197 106 +189 164 57 +139 106 49 +255 255 123 +172 172 123 +222 222 197 +115 90 74 +74 65 57 +189 189 213 +0 0 0 +131 123 115 +255 255 255 diff --git a/graphics/types/move_types_2.pal b/graphics/types/move_types_2.pal new file mode 100644 index 000000000..08dcea47c --- /dev/null +++ b/graphics/types/move_types_2.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +205 197 255 +172 148 246 +115 90 156 +106 148 246 +156 222 222 +213 255 238 +74 57 82 +255 90 139 +255 197 180 +148 98 98 +164 65 164 +222 131 189 +148 148 164 +131 123 115 +255 255 255 diff --git a/graphics/types/move_types_3.pal b/graphics/types/move_types_3.pal new file mode 100644 index 000000000..4db37235b --- /dev/null +++ b/graphics/types/move_types_3.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +197 255 98 +123 205 82 +90 131 65 +115 57 255 +74 57 148 +189 164 255 +172 189 32 +222 230 49 +123 148 16 +106 164 148 +115 205 180 +32 106 98 +0 0 0 +131 123 115 +255 255 255 diff --git a/graphics/types/mystery.png b/graphics/types/mystery.png new file mode 100644 index 000000000..a2ea01d55 Binary files /dev/null and b/graphics/types/mystery.png differ diff --git a/graphics/types/none.png b/graphics/types/none.png new file mode 100644 index 000000000..a2ea01d55 Binary files /dev/null and b/graphics/types/none.png differ diff --git a/graphics/types/normal.png b/graphics/types/normal.png new file mode 100644 index 000000000..608a1f9c9 Binary files /dev/null and b/graphics/types/normal.png differ diff --git a/graphics/types/poison.png b/graphics/types/poison.png new file mode 100644 index 000000000..502dfae37 Binary files /dev/null and b/graphics/types/poison.png differ diff --git a/graphics/types/psychic.png b/graphics/types/psychic.png new file mode 100644 index 000000000..975d1fb55 Binary files /dev/null and b/graphics/types/psychic.png differ diff --git a/graphics/types/rock.png b/graphics/types/rock.png new file mode 100644 index 000000000..3793597f9 Binary files /dev/null and b/graphics/types/rock.png differ diff --git a/graphics/types/steel.png b/graphics/types/steel.png new file mode 100644 index 000000000..29748639c Binary files /dev/null and b/graphics/types/steel.png differ diff --git a/graphics/types/stellar.png b/graphics/types/stellar.png new file mode 100644 index 000000000..ee18b997e Binary files /dev/null and b/graphics/types/stellar.png differ diff --git a/graphics/types/water.png b/graphics/types/water.png new file mode 100644 index 000000000..19ae3ebe3 Binary files /dev/null and b/graphics/types/water.png differ diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 3fea4c16b..4464238e7 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -158,7 +158,7 @@ enum CONTROLLER_PRINTSTRING, CONTROLLER_PRINTSTRINGPLAYERONLY, CONTROLLER_CHOOSEACTION, - CONTROLLER_UNKNOWNYESNOBOX, + CONTROLLER_YESNOBOX, CONTROLLER_CHOOSEMOVE, CONTROLLER_OPENBAG, CONTROLLER_CHOOSEPOKEMON, @@ -307,17 +307,23 @@ void SetBattleEndCallbacks(u32 battler); void SetControllerToPlayer(u32 battler); void PlayerHandleBallThrowAnim(u32 battler); void SpriteCB_FreePlayerSpriteLoadMonSpriteOld(struct Sprite *sprite); -void SetCB2ToReshowScreenAfterMenu(void); -void SetCB2ToReshowScreenAfterMenu2(void); +void CB2_SetUpReshowBattleScreenAfterMenu(void); +void CB2_SetUpReshowBattleScreenAfterMenu2(void); void Task_PlayerController_RestoreBgmAfterCry(u8 taskId); void ActionSelectionCreateCursorAt(u8 cursorPos, u8 unused); void ActionSelectionDestroyCursorAt(u8 cursorPos); void InitMoveSelectionsVarsAndStrings(u32 battler); void MoveSelectionCreateCursorAt(u8 cursorPos, u8 arg1); void MoveSelectionDestroyCursorAt(u8 cursorPos); -void HandleInputChooseMove(u32 battler); u32 LinkPlayerGetTrainerPicId(u32 multiplayerId); void PlayerHandleExpUpdate(u32 battler); +void PlayerHandleChooseMove(u32 battler); +void HandleInputChooseMove(u32 battler); +void HandleInputChooseTarget(u32 battler); +void HandleInputShowEntireFieldTargets(u32 battler); +void HandleInputShowTargets(u32 battler); +void HandleMoveSwitching(u32 battler); +void HandleChooseMoveAfterDma3(u32 battler); // recorded player controller void SetControllerToRecordedPlayer(u32 battler); diff --git a/include/battle_main.h b/include/battle_main.h index 6751a7d97..10826d1b7 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -96,6 +96,7 @@ s8 GetChosenMovePriority(u32 battlerId); u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect); u32 GetBattlerTotalSpeedStat(u32 battler); void SpecialStatusesClear(void); +u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, u8 *ateBoost); s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2, u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2); s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenMoves); diff --git a/include/graphics.h b/include/graphics.h index f59e18db6..4cb514ec2 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -6523,4 +6523,9 @@ extern const u32 gBattleAnimBgImage_Swamp[]; extern const u32 gBattleAnimBGPalette_Swamp[]; extern const u32 gBattleAnimBgTilemap_Swamp[]; +extern const u32 gBattleIcons_Gfx1[]; +extern const u32 gBattleIcons_Gfx2[]; +extern const u32 gBattleIcons_Pal1[]; +extern const u32 gBattleIcons_Pal2[]; + #endif //GUARD_GRAPHICS_H diff --git a/include/item.h b/include/item.h index 7f3a9f3aa..1d4448e41 100644 --- a/include/item.h +++ b/include/item.h @@ -18,7 +18,9 @@ struct Item u8 pluralName[ITEM_NAME_PLURAL_LENGTH]; u8 holdEffect; u8 holdEffectParam; - u8 importance; + u8 importance:2; + u8 notConsumed:1; + u8 padding:5; u8 pocket; u8 type; u8 battleUsage; @@ -57,6 +59,7 @@ u8 ItemId_GetHoldEffectParam(u16 itemId); const u8 *ItemId_GetDescription(u16 itemId); bool32 ItemId_CopyDescription(u8 *a, u32 itemId, u32 c); u8 ItemId_GetImportance(u16 itemId); +u8 ItemId_GetConsumability(u16 itemId); u8 ItemId_GetUnknownValue(u16 itemId); u8 ItemId_GetPocket(u16 itemId); u8 ItemId_GetType(u16 itemId); diff --git a/include/item_menu.h b/include/item_menu.h index f88a40d42..69c88efcb 100644 --- a/include/item_menu.h +++ b/include/item_menu.h @@ -24,7 +24,7 @@ extern struct BagStruct gBagMenuState; extern u16 gSpecialVar_ItemId; // Exported ROM declarations -void CB2_SetUpReshowBattleScreenAfterMenu(void); +void SetBagOpenFalse(void); void ResetBagCursorPositions(void); void InitPokedudeBag(u8); void CB2_BagMenuFromStartMenu(void); diff --git a/include/line_break.h b/include/line_break.h new file mode 100644 index 000000000..c423c9bf4 --- /dev/null +++ b/include/line_break.h @@ -0,0 +1,33 @@ +#ifndef GUARD_LINE_BREAK_H +#define GUARD_LINE_BREAK_H + +#define BADNESS_UNFILLED 1 // Badness added per pixel diff from max width +#define BADNESS_JAGGED 1 // Badness added per pixel diff from longest, squared per line +#define BADNESS_RUNT 100 // Badness added if there's a runt +#define BADNESS_OVERFLOW 100 // Badness added per pixel overflow, squared per line (not used) +#define BADNESS_WIDE_SPACE 1 // Badness added per extra pixel width (not used) +#define MAX_SPACE_WIDTH 5 + +struct StringWord { + u32 startIndex:16; + u32 length:8; + u32 width:8; +}; + +struct StringLine { + struct StringWord *words; + u16 numWords; + u8 spaceWidth; + u8 extraSpaceWidth; +}; + +void StripLineBreaks(u8 *src); +void BreakStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId); +void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId); + +bool32 IsWordSplittingChar(const u8 *src, u32 index); +u32 GetStringBadness(struct StringLine *stringLines, u32 numLines, u32 maxWidth); +void BuildNewString(struct StringLine *stringLines, u32 numLines, u32 maxLines, u8 *str); +bool32 StringHasManualBreaks(u8 *src); + +#endif // GUARD_LINE_BREAK_H diff --git a/include/party_menu.h b/include/party_menu.h index fddde710e..3250043a6 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -78,7 +78,7 @@ void ClearSelectedPartyOrder(void); void ChooseMonForTradingBoard(u8 menuType, MainCallback callback); void ChooseMonForMoveTutor(void); void ChooseMonForWirelessMinigame(void); -void OpenPartyMenuInTutorialBattle(u8 partyAction); +void OpenPartyMenuInBattle(u8 partyAction); void Pokedude_OpenPartyMenuInBattle(void); void Pokedude_ChooseMonForInBattleItem(void); void EnterPartyFromItemMenuInBattle(void); diff --git a/include/pokemon.h b/include/pokemon.h index c62e135c6..c44d56713 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -782,5 +782,6 @@ u16 HoennNumToSpecies(u16 hoennNum); u8 CopyMonToPC(struct Pokemon *mon); void UpdateDaysPassedSinceFormChange(u16 days); void TrySetDayLimitToFormChange(struct Pokemon *mon); +u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler); #endif // GUARD_POKEMON_H diff --git a/include/type_icons.h b/include/type_icons.h new file mode 100644 index 000000000..1d6a312f0 --- /dev/null +++ b/include/type_icons.h @@ -0,0 +1,18 @@ +#ifndef GUARD_TYPE_ICONS_H +#define GUARD_TYPE_ICONS_H + +void LoadTypeIcons(u32); + +#define TYPE_ICON_TAG 0x2720 +#define TYPE_ICON_TAG_2 0x2721 +#define NUM_FRAMES_HIDE_TYPE_ICON 10 + +#define tMonPosition data[0] +#define tBattlerId data[1] +#define tHideIconTimer data[2] +#define tVerticalPosition data[3] + +#define TYPE_ICON_1_FRAME(monType) ((monType - 1) * 2) +#define TYPE_ICON_2_FRAME(monType) ((monType - 11) * 2) + +#endif // GUARD_TYPE_ICONS_H diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index d04976fd7..ee1293c10 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -52,7 +52,7 @@ static void (*const sLinkOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 batt [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, // done [CONTROLLER_CHOOSEACTION] = BtlController_Empty, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = BtlController_Empty, // done [CONTROLLER_OPENBAG] = BtlController_Empty, // done [CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty, // done diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index b540805df..48b81e918 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -49,7 +49,7 @@ static void (*const sLinkPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battl [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, // done [CONTROLLER_CHOOSEACTION] = BtlController_Empty, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = BtlController_Empty, // done [CONTROLLER_OPENBAG] = BtlController_Empty, // done [CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty, // done diff --git a/src/battle_controller_oak_old_man.c b/src/battle_controller_oak_old_man.c index 6e4a9eebf..af66b3a6b 100644 --- a/src/battle_controller_oak_old_man.c +++ b/src/battle_controller_oak_old_man.c @@ -70,7 +70,7 @@ static void (*const sOakOldManBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler [CONTROLLER_PRINTSTRING] = OakOldManHandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = OakOldManHandlePrintSelectionString, // done [CONTROLLER_CHOOSEACTION] = OakOldManHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = OakOldManHandleChooseMove, // done [CONTROLLER_OPENBAG] = OakOldManHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = OakOldManHandleChoosePokemon, // done @@ -291,7 +291,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) caseId = gTasks[gBattleControllerData[battler]].data[0]; DestroyTask(gBattleControllerData[battler]); FreeAllWindowBuffers(); - OpenPartyMenuInTutorialBattle(caseId); + OpenPartyMenuInBattle(caseId); } } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index a815bc6da..744762f9a 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -68,7 +68,7 @@ static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, // done [CONTROLLER_CHOOSEACTION] = OpponentHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = OpponentHandleChooseMove, // done [CONTROLLER_OPENBAG] = OpponentHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = OpponentHandleChoosePokemon, // done diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 51738f164..e9c281ac9 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1,37 +1,50 @@ #include "global.h" -#include "battle_controllers.h" -#include "gflib.h" -#include "data.h" -#include "item.h" -#include "item_menu.h" -#include "link.h" -#include "m4a.h" -#include "menu.h" -#include "party_menu.h" -#include "pokeball.h" -#include "strings.h" -#include "pokemon_special_anim.h" -#include "task.h" -#include "util.h" #include "battle.h" #include "battle_anim.h" +#include "battle_controllers.h" #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" -#include "battle_script_commands.h" #include "battle_z_move.h" #include "battle_gimmick.h" -#include "pokemon_summary_screen.h" +#include "bg.h" +#include "data.h" +#include "item.h" +#include "item_menu.h" +#include "line_break.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "party_menu.h" +#include "pokeball.h" +#include "pokemon.h" +#include "pokemon_special_anim.h" +#include "random.h" #include "recorded_battle.h" #include "reshow_battle_screen.h" +#include "sound.h" +#include "string_util.h" +#include "strings.h" +#include "task.h" #include "test_runner.h" +#include "text.h" +#include "type_icons.h" +#include "util.h" +#include "window.h" #include "constants/battle_anim.h" #include "constants/battle_move_effects.h" #include "constants/battle_partner.h" +#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" +#include "constants/party_menu.h" #include "constants/songs.h" -#include "constants/sound.h" +#include "constants/trainers.h" +#include "constants/rgb.h" +#include "caps.h" +#include "menu.h" +#include "pokemon_summary_screen.h" static void PlayerBufferExecCompleted(u32 battler); static void PlayerHandleLoadMonSprite(u32 battler); @@ -42,9 +55,11 @@ static void PlayerHandleTrainerSlideBack(u32 battler); static void PlayerHandlePaletteFade(u32 battler); static void PlayerHandleSuccessBallThrowAnim(u32 battler); static void PlayerHandlePause(u32 battler); +static void PlayerHandleMoveAnimation(u32 battler); +static void PlayerHandlePrintString(u32 battler); static void PlayerHandlePrintSelectionString(u32 battler); static void PlayerHandleChooseAction(u32 battler); -static void PlayerHandleChooseMove(u32 battler); +static void PlayerHandleYesNoBox(u32 battler); static void PlayerHandleChooseItem(u32 battler); static void PlayerHandleChoosePokemon(u32 battler); static void PlayerHandleCmd23(u32 battler); @@ -66,86 +81,84 @@ static void PlayerHandleEndLinkBattle(u32 battler); static void PlayerHandleBattleDebug(u32 battler); static void PlayerBufferRunCommand(u32 battler); -static void HandleInputChooseTarget(u32 battler); static void MoveSelectionDisplayPpNumber(u32 battler); -static void MoveSelectionDisplayPpString(void); +static void MoveSelectionDisplayPpString(u32 battler); static void MoveSelectionDisplayMoveType(u32 battler); static void MoveSelectionDisplayMoveNames(u32 battler); static void MoveSelectionDisplayMoveDescription(u32 battler); -static void HandleMoveSwitching(u32 battler); +static void SwitchIn_HandleSoundAndEnd(u32 battler); static void WaitForMonSelection(u32 battler); static void CompleteWhenChoseItem(u32 battler); -static void Task_LaunchLvlUpAnim(u8 taskId); -static void Task_PrepareToGiveExpWithExpBar(u8 taskId); -static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId); -static void Task_UpdateLvlInHealthbox(u8 taskId); +static void Task_LaunchLvlUpAnim(u8); +static void Task_PrepareToGiveExpWithExpBar(u8); +static void Task_SetControllerToWaitForString(u8); +static void Task_GiveExpWithExpBar(u8); +static void Task_UpdateLvlInHealthbox(u8); static void PrintLinkStandbyMsg(void); -static void SwitchIn_HandleSoundAndEnd(u32 battler); -static void Task_GiveExpWithExpBar(u8 taskId); static void Task_CreateLevelUpVerticalStripes(u8 taskId); static void ReloadMoveNames(u32 battler); static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = { - [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, // done - [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, // done - [CONTROLLER_SETMONDATA] = BtlController_HandleSetMonData, // done - [CONTROLLER_SETRAWMONDATA] = BtlController_HandleSetRawMonData, // done - [CONTROLLER_LOADMONSPRITE] = PlayerHandleLoadMonSprite, // done - [CONTROLLER_SWITCHINANIM] = PlayerHandleSwitchInAnim, // done - [CONTROLLER_RETURNMONTOBALL] = BtlController_HandleReturnMonToBall, // done - [CONTROLLER_DRAWTRAINERPIC] = PlayerHandleDrawTrainerPic, // done - [CONTROLLER_TRAINERSLIDE] = PlayerHandleTrainerSlide, // done - [CONTROLLER_TRAINERSLIDEBACK] = PlayerHandleTrainerSlideBack, // done - [CONTROLLER_FAINTANIMATION] = BtlController_HandleFaintAnimation, // done - [CONTROLLER_PALETTEFADE] = PlayerHandlePaletteFade, // done - [CONTROLLER_SUCCESSBALLTHROWANIM] = PlayerHandleSuccessBallThrowAnim, // done - [CONTROLLER_BALLTHROWANIM] = PlayerHandleBallThrowAnim, // done - [CONTROLLER_PAUSE] = PlayerHandlePause, // done - [CONTROLLER_MOVEANIMATION] = BtlController_HandleMoveAnimation, // done - [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done - [CONTROLLER_PRINTSTRINGPLAYERONLY] = PlayerHandlePrintSelectionString, // done - [CONTROLLER_CHOOSEACTION] = PlayerHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done - [CONTROLLER_CHOOSEMOVE] = PlayerHandleChooseMove, // done - [CONTROLLER_OPENBAG] = PlayerHandleChooseItem, // done - [CONTROLLER_CHOOSEPOKEMON] = PlayerHandleChoosePokemon, // done - [CONTROLLER_23] = PlayerHandleCmd23, // done - [CONTROLLER_HEALTHBARUPDATE] = PlayerHandleHealthBarUpdate, // done - [CONTROLLER_EXPUPDATE] = PlayerHandleExpUpdate, // done - [CONTROLLER_STATUSICONUPDATE] = BtlController_HandleStatusIconUpdate, // done - [CONTROLLER_STATUSANIMATION] = BtlController_HandleStatusAnimation, // done - [CONTROLLER_STATUSXOR] = PlayerHandleStatusXor, // done - [CONTROLLER_DATATRANSFER] = BtlController_Empty, // done - [CONTROLLER_DMA3TRANSFER] = PlayerHandleDMA3Transfer, // done - [CONTROLLER_PLAYBGM] = PlayerHandlePlayBGM, // done - [CONTROLLER_32] = BtlController_Empty, // done - [CONTROLLER_TWORETURNVALUES] = PlayerHandleTwoReturnValues, // done - [CONTROLLER_CHOSENMONRETURNVALUE] = PlayerHandleChosenMonReturnValue, // done - [CONTROLLER_ONERETURNVALUE] = PlayerHandleOneReturnValue, // done - [CONTROLLER_ONERETURNVALUE_DUPLICATE] = PlayerHandleOneReturnValue_Duplicate, // done - [CONTROLLER_CLEARUNKVAR] = BtlController_HandleClearUnkVar, // done - [CONTROLLER_SETUNKVAR] = BtlController_HandleSetUnkVar, // done - [CONTROLLER_CLEARUNKFLAG] = BtlController_HandleClearUnkFlag, // done - [CONTROLLER_TOGGLEUNKFLAG] = BtlController_HandleToggleUnkFlag, // done - [CONTROLLER_HITANIMATION] = BtlController_HandleHitAnimation, // done - [CONTROLLER_CANTSWITCH] = BtlController_Empty, // done - [CONTROLLER_PLAYSE] = BtlController_HandlePlaySE, // done - [CONTROLLER_PLAYFANFAREORBGM] = BtlController_HandlePlayFanfareOrBGM, // done - [CONTROLLER_FAINTINGCRY] = BtlController_HandleFaintingCry, // done - [CONTROLLER_INTROSLIDE] = BtlController_HandleIntroSlide, // done - [CONTROLLER_INTROTRAINERBALLTHROW] = PlayerHandleIntroTrainerBallThrow, // done - [CONTROLLER_DRAWPARTYSTATUSSUMMARY] = PlayerHandleDrawPartyStatusSummary, // done - [CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_HandleHidePartyStatusSummary, // done - [CONTROLLER_ENDBOUNCE] = PlayerHandleEndBounceEffect, // done - [CONTROLLER_SPRITEINVISIBILITY] = BtlController_HandleSpriteInvisibility, // done - [CONTROLLER_BATTLEANIMATION] = PlayerHandleBattleAnimation, // done - [CONTROLLER_LINKSTANDBYMSG] = PlayerHandleLinkStandbyMsg, // done - [CONTROLLER_RESETACTIONMOVESELECTION] = PlayerHandleResetActionMoveSelection, // done - [CONTROLLER_ENDLINKBATTLE] = PlayerHandleEndLinkBattle, // done + [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, + [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, + [CONTROLLER_SETMONDATA] = BtlController_HandleSetMonData, + [CONTROLLER_SETRAWMONDATA] = BtlController_HandleSetRawMonData, + [CONTROLLER_LOADMONSPRITE] = PlayerHandleLoadMonSprite, + [CONTROLLER_SWITCHINANIM] = PlayerHandleSwitchInAnim, + [CONTROLLER_RETURNMONTOBALL] = BtlController_HandleReturnMonToBall, + [CONTROLLER_DRAWTRAINERPIC] = PlayerHandleDrawTrainerPic, + [CONTROLLER_TRAINERSLIDE] = PlayerHandleTrainerSlide, + [CONTROLLER_TRAINERSLIDEBACK] = PlayerHandleTrainerSlideBack, + [CONTROLLER_FAINTANIMATION] = BtlController_HandleFaintAnimation, + [CONTROLLER_PALETTEFADE] = PlayerHandlePaletteFade, + [CONTROLLER_SUCCESSBALLTHROWANIM] = PlayerHandleSuccessBallThrowAnim, + [CONTROLLER_BALLTHROWANIM] = PlayerHandleBallThrowAnim, + [CONTROLLER_PAUSE] = PlayerHandlePause, + [CONTROLLER_MOVEANIMATION] = PlayerHandleMoveAnimation, + [CONTROLLER_PRINTSTRING] = PlayerHandlePrintString, + [CONTROLLER_PRINTSTRINGPLAYERONLY] = PlayerHandlePrintSelectionString, + [CONTROLLER_CHOOSEACTION] = PlayerHandleChooseAction, + [CONTROLLER_YESNOBOX] = PlayerHandleYesNoBox, + [CONTROLLER_CHOOSEMOVE] = PlayerHandleChooseMove, + [CONTROLLER_OPENBAG] = PlayerHandleChooseItem, + [CONTROLLER_CHOOSEPOKEMON] = PlayerHandleChoosePokemon, + [CONTROLLER_23] = PlayerHandleCmd23, + [CONTROLLER_HEALTHBARUPDATE] = PlayerHandleHealthBarUpdate, + [CONTROLLER_EXPUPDATE] = PlayerHandleExpUpdate, + [CONTROLLER_STATUSICONUPDATE] = BtlController_HandleStatusIconUpdate, + [CONTROLLER_STATUSANIMATION] = BtlController_HandleStatusAnimation, + [CONTROLLER_STATUSXOR] = PlayerHandleStatusXor, + [CONTROLLER_DATATRANSFER] = BtlController_Empty, + [CONTROLLER_DMA3TRANSFER] = PlayerHandleDMA3Transfer, + [CONTROLLER_PLAYBGM] = PlayerHandlePlayBGM, + [CONTROLLER_32] = BtlController_Empty, + [CONTROLLER_TWORETURNVALUES] = PlayerHandleTwoReturnValues, + [CONTROLLER_CHOSENMONRETURNVALUE] = PlayerHandleChosenMonReturnValue, + [CONTROLLER_ONERETURNVALUE] = PlayerHandleOneReturnValue, + [CONTROLLER_ONERETURNVALUE_DUPLICATE] = PlayerHandleOneReturnValue_Duplicate, + [CONTROLLER_CLEARUNKVAR] = BtlController_HandleClearUnkVar, + [CONTROLLER_SETUNKVAR] = BtlController_HandleSetUnkVar, + [CONTROLLER_CLEARUNKFLAG] = BtlController_HandleClearUnkFlag, + [CONTROLLER_TOGGLEUNKFLAG] = BtlController_HandleToggleUnkFlag, + [CONTROLLER_HITANIMATION] = BtlController_HandleHitAnimation, + [CONTROLLER_CANTSWITCH] = BtlController_Empty, + [CONTROLLER_PLAYSE] = BtlController_HandlePlaySE, + [CONTROLLER_PLAYFANFAREORBGM] = BtlController_HandlePlayFanfareOrBGM, + [CONTROLLER_FAINTINGCRY] = BtlController_HandleFaintingCry, + [CONTROLLER_INTROSLIDE] = BtlController_HandleIntroSlide, + [CONTROLLER_INTROTRAINERBALLTHROW] = PlayerHandleIntroTrainerBallThrow, + [CONTROLLER_DRAWPARTYSTATUSSUMMARY] = PlayerHandleDrawPartyStatusSummary, + [CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_HandleHidePartyStatusSummary, + [CONTROLLER_ENDBOUNCE] = PlayerHandleEndBounceEffect, + [CONTROLLER_SPRITEINVISIBILITY] = BtlController_HandleSpriteInvisibility, + [CONTROLLER_BATTLEANIMATION] = PlayerHandleBattleAnimation, + [CONTROLLER_LINKSTANDBYMSG] = PlayerHandleLinkStandbyMsg, + [CONTROLLER_RESETACTIONMOVESELECTION] = PlayerHandleResetActionMoveSelection, + [CONTROLLER_ENDLINKBATTLE] = PlayerHandleEndLinkBattle, [CONTROLLER_DEBUGMENU] = PlayerHandleBattleDebug, - [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop, // done + [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; void SetControllerToPlayer(u32 battler) @@ -168,15 +181,15 @@ static void PlayerBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~gBitTable[battler]; + gBattleControllerExecFlags &= ~(1u << battler); } } static void PlayerBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & gBitTable[battler]) + if (gBattleControllerExecFlags & (1u << battler)) { - if (gBattleResources->bufferA[battler][0] < NELEMS(sPlayerBufferCommands)) + if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerBufferCommands)) sPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler); else PlayerBufferExecCompleted(battler); @@ -240,6 +253,11 @@ static void HandleInputChooseAction(u32 battler) DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); + if (JOY_REPT(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) + gPlayerDpadHoldFrames++; + else + gPlayerDpadHoldFrames = 0; + if (B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == TRUE) { if (!gLastUsedBallMenuPresent) @@ -305,16 +323,16 @@ static void HandleInputChooseAction(u32 battler) switch (gActionSelectionCursor[battler]) { - case 0: + case 0: // Top left BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_MOVE, 0); break; - case 1: + case 1: // Top right BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_ITEM, 0); break; - case 2: + case 2: // Bottom left BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SWITCH, 0); break; - case 3: + case 3: // Bottom right BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0); break; } @@ -360,16 +378,16 @@ static void HandleInputChooseAction(u32 battler) ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0); } } - else if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) { - if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (IsDoubleBattle() && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT - && !(gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]) + && !(gAbsentBattlerFlags & (1u << GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - if (gBattleResources->bufferA[battler][1] == B_ACTION_USE_ITEM) + // Return item to bag if partner had selected one (if consumable). + if (gBattleResources->bufferA[battler][1] == B_ACTION_USE_ITEM && ItemId_GetConsumability(itemId)) { - // Return item to bag if partner had selected one. AddBagItem(itemId, 1); } PlaySE(SE_SELECT); @@ -406,7 +424,7 @@ static void HandleInputChooseAction(u32 battler) } } -static void HandleInputChooseTarget(u32 battler) +void HandleInputChooseTarget(u32 battler) { s32 i; static const u8 identities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT}; @@ -420,6 +438,11 @@ static void HandleInputChooseTarget(u32 battler) EndBounceEffect(i, BOUNCE_HEALTHBOX); } + if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) + gPlayerDpadHoldFrames++; + else + gPlayerDpadHoldFrames = 0; + if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); @@ -433,7 +456,7 @@ static void HandleInputChooseTarget(u32 battler) HideGimmickTriggerSprite(); PlayerBufferExecCompleted(battler); } - else if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) { PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget; @@ -485,7 +508,7 @@ static void HandleInputChooseTarget(u32 battler) break; } - if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor] + if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move)) i = 0; } while (i == 0); @@ -535,7 +558,7 @@ static void HandleInputChooseTarget(u32 battler) break; } - if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor] + if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move)) i = 0; } while (i == 0); @@ -571,7 +594,7 @@ static void HideShownTargets(u32 battler) } } -static void HandleInputShowEntireFieldTargets(u32 battler) +void HandleInputShowEntireFieldTargets(u32 battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -599,7 +622,7 @@ static void HandleInputShowEntireFieldTargets(u32 battler) } } -static void HandleInputShowTargets(u32 battler) +void HandleInputShowTargets(u32 battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -657,13 +680,13 @@ void HandleInputChooseMove(u32 battler) if (gBattleStruct->zmove.viewing) { gBattleStruct->zmove.viewing = FALSE; - if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category != DAMAGE_CATEGORY_STATUS) + if (GetMoveCategory(moveInfo->moves[gMoveSelectionCursor[battler]]) != DAMAGE_CATEGORY_STATUS) moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target } // Status moves turn into Max Guard when Dynamaxed, targets user. if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)) - moveTarget = gMovesInfo[GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])].target; + moveTarget = GetMoveTarget(GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])); if (moveTarget & MOVE_TARGET_USER) gMultiUsePlayerCursor = battler; @@ -731,7 +754,7 @@ void HandleInputChooseMove(u32 battler) if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) gMultiUsePlayerCursor = battler; - else if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)]) + else if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); else gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); @@ -865,7 +888,7 @@ void HandleInputChooseMove(u32 battler) } else if (JOY_NEW(START_BUTTON)) { - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) + if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE && !HasTrainerUsedGimmick(battler, gBattleStruct->gimmick.usableGimmick[battler])) { gBattleStruct->gimmick.playerSelect ^= 1; ReloadMoveNames(battler); @@ -875,7 +898,6 @@ void HandleInputChooseMove(u32 battler) } } - static void ReloadMoveNames(u32 battler) { if (gBattleStruct->zmove.viable && !gBattleStruct->zmove.viewing) @@ -894,14 +916,65 @@ static void ReloadMoveNames(u32 battler) } } -static void HandleMoveSwitching(u32 battler) +static u32 UNUSED HandleMoveInputUnused(u32 battler) { - u8 perMovePPBonuses[4]; + u32 var = 0; + + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + var = 1; + } + if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattle_BG0_X = 0; + gBattle_BG0_Y = DISPLAY_HEIGHT * 2; + var = 0xFF; + } + if (JOY_NEW(DPAD_LEFT) && gMoveSelectionCursor[battler] & 1) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_RIGHT) && !(gMoveSelectionCursor[battler] & 1) + && (gMoveSelectionCursor[battler] ^ 1) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_UP) && gMoveSelectionCursor[battler] & 2) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_DOWN) && !(gMoveSelectionCursor[battler] & 2) + && (gMoveSelectionCursor[battler] ^ 2) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + + return var; +} + +void HandleMoveSwitching(u32 battler) +{ + u8 perMovePPBonuses[MAX_MON_MOVES]; struct ChooseMoveStruct moveStruct; u8 totalPPBonuses; if (JOY_NEW(A_BUTTON | SELECT_BUTTON)) { + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); PlaySE(SE_SELECT); if (gMoveSelectionCursor[battler] != gMultiUsePlayerCursor) @@ -913,75 +986,89 @@ static void HandleMoveSwitching(u32 battler) i = moveInfo->moves[gMoveSelectionCursor[battler]]; moveInfo->moves[gMoveSelectionCursor[battler]] = moveInfo->moves[gMultiUsePlayerCursor]; moveInfo->moves[gMultiUsePlayerCursor] = i; + i = moveInfo->currentPp[gMoveSelectionCursor[battler]]; moveInfo->currentPp[gMoveSelectionCursor[battler]] = moveInfo->currentPp[gMultiUsePlayerCursor]; moveInfo->currentPp[gMultiUsePlayerCursor] = i; + i = moveInfo->maxPp[gMoveSelectionCursor[battler]]; moveInfo->maxPp[gMoveSelectionCursor[battler]] = moveInfo->maxPp[gMultiUsePlayerCursor]; moveInfo->maxPp[gMultiUsePlayerCursor] = i; - if (gDisableStructs[battler].mimickedMoves & gBitTable[gMoveSelectionCursor[battler]]) + + if (gDisableStructs[battler].mimickedMoves & (1u << gMoveSelectionCursor[battler])) { - gDisableStructs[battler].mimickedMoves &= (~gBitTable[gMoveSelectionCursor[battler]]); - gDisableStructs[battler].mimickedMoves |= gBitTable[gMultiUsePlayerCursor]; + gDisableStructs[battler].mimickedMoves &= ~(1u << gMoveSelectionCursor[battler]); + gDisableStructs[battler].mimickedMoves |= 1u << gMultiUsePlayerCursor; } + MoveSelectionDisplayMoveNames(battler); - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) perMovePPBonuses[i] = (gBattleMons[battler].ppBonuses & (3 << (i * 2))) >> (i * 2); + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[battler]]; perMovePPBonuses[gMoveSelectionCursor[battler]] = perMovePPBonuses[gMultiUsePlayerCursor]; perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) totalPPBonuses |= perMovePPBonuses[i] << (i * 2); gBattleMons[battler].ppBonuses = totalPPBonuses; - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) { gBattleMons[battler].moves[i] = moveInfo->moves[i]; gBattleMons[battler].pp[i] = moveInfo->currentPp[i]; } + if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) { - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) { moveStruct.moves[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_MOVE1 + i); moveStruct.currentPp[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP1 + i); } totalPPBonuses = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP_BONUSES); - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) perMovePPBonuses[i] = (totalPPBonuses & (3 << (i * 2))) >> (i * 2); + i = moveStruct.moves[gMoveSelectionCursor[battler]]; moveStruct.moves[gMoveSelectionCursor[battler]] = moveStruct.moves[gMultiUsePlayerCursor]; moveStruct.moves[gMultiUsePlayerCursor] = i; + i = moveStruct.currentPp[gMoveSelectionCursor[battler]]; moveStruct.currentPp[gMoveSelectionCursor[battler]] = moveStruct.currentPp[gMultiUsePlayerCursor]; moveStruct.currentPp[gMultiUsePlayerCursor] = i; + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[battler]]; perMovePPBonuses[gMoveSelectionCursor[battler]] = perMovePPBonuses[gMultiUsePlayerCursor]; perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) totalPPBonuses |= perMovePPBonuses[i] << (i * 2); - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) { SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_MOVE1 + i, &moveStruct.moves[i]); SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP1 + i, &moveStruct.currentPp[i]); } + SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP_BONUSES, &totalPPBonuses); } } - if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - gBattlerControllerFuncs[battler] = OakOldManHandleInputChooseMove; - else - gBattlerControllerFuncs[battler] = HandleInputChooseMove; + + gBattlerControllerFuncs[battler] = HandleInputChooseMove; gMoveSelectionCursor[battler] = gMultiUsePlayerCursor; MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); + AssignUsableZMoves(battler, moveInfo->moves); } - if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON | SELECT_BUTTON)) { PlaySE(SE_SELECT); MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); @@ -990,11 +1077,11 @@ static void HandleMoveSwitching(u32 battler) gBattlerControllerFuncs[battler] = OakOldManHandleInputChooseMove; else gBattlerControllerFuncs[battler] = HandleInputChooseMove; - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); } - if (JOY_NEW(DPAD_LEFT)) + else if (JOY_NEW(DPAD_LEFT)) { if (gMultiUsePlayerCursor & 1) { @@ -1002,15 +1089,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_RIGHT)) + else if (JOY_NEW(DPAD_RIGHT)) { if (!(gMultiUsePlayerCursor & 1) && (gMultiUsePlayerCursor ^ 1) < gNumberOfMovesToChoose) { @@ -1018,15 +1107,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_UP)) + else if (JOY_NEW(DPAD_UP)) { if (gMultiUsePlayerCursor & 2) { @@ -1034,15 +1125,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_DOWN)) + else if (JOY_NEW(DPAD_DOWN)) { if (!(gMultiUsePlayerCursor & 2) && (gMultiUsePlayerCursor ^ 2) < gNumberOfMovesToChoose) { @@ -1050,8 +1143,10 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else @@ -1064,36 +1159,44 @@ static void SetLinkBattleEndCallbacks(u32 battler) { if (gWirelessCommType == 0) { - if (!gReceivedRemoteLinkPlayers) + if (gReceivedRemoteLinkPlayers == 0) { m4aSongNumStop(SE_LOW_HEALTH); - gMain.inBattle = 0; + gMain.inBattle = FALSE; gMain.callback1 = gPreBattleCallback1; SetMainCallback2(CB2_InitEndLinkBattle); FreeAllWindowBuffers(); } } - else if (IsLinkTaskFinished()) + else { - m4aSongNumStop(SE_LOW_HEALTH); - gMain.inBattle = 0; - gMain.callback1 = gPreBattleCallback1; - SetMainCallback2(CB2_InitEndLinkBattle); - FreeAllWindowBuffers(); + if (IsLinkTaskFinished()) + { + m4aSongNumStop(SE_LOW_HEALTH); + gMain.inBattle = FALSE; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(CB2_InitEndLinkBattle); + FreeAllWindowBuffers(); + } } } +// Despite handling link battles separately, this is only ever used by link battles void SetBattleEndCallbacks(u32 battler) { if (!gPaletteFade.active) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { - if (gWirelessCommType == 0) - SetCloseLinkCallback(); - else - SetLinkStandbyCallback(); - gBattlerControllerFuncs[battler] = SetLinkBattleEndCallbacks; + if (IsLinkTaskFinished()) + { + if (gWirelessCommType == 0) + SetCloseLinkCallback(); + else + SetLinkStandbyCallback(); + + gBattlerControllerFuncs[battler] = SetLinkBattleEndCallbacks; + } } else { @@ -1242,14 +1345,21 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy - && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { + CopyBattleSpriteInvisibility(battler); + + // Reset shiny anim (even if it didn't occur) gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + + // Check if Substitute should be shown if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[battler] = SwitchIn_HandleSoundAndEnd; } } @@ -1257,7 +1367,7 @@ static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) static void SwitchIn_HandleSoundAndEnd(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive - && !IsCryPlayingOrClearCrySongs()) + && !IsCryPlayingOrClearCrySongs()) { m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); @@ -1267,19 +1377,19 @@ static void SwitchIn_HandleSoundAndEnd(u32 battler) static void SwitchIn_TryShinyAnimShowHealthbox(u32 battler) { + // Start shiny animation if applicable if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim - && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); + + // Wait for ball anim, then show healthbox if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy - && !(gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive)) + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) { DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], - &gPlayerParty[gBattlerPartyIndexes[battler]], - HEALTHBOX_ALL); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); StartHealthboxSlideIn(battler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); - CopyBattleSpriteInvisibility(battler); gBattlerControllerFuncs[battler] = SwitchIn_CleanShinyAnimShowSubstitute; } } @@ -1293,17 +1403,11 @@ void Task_PlayerController_RestoreBgmAfterCry(u8 taskId) } } -void CompleteOnInactiveTextPrinter(u32 battler) -{ - if (!IsTextPrinterActive(0)) - PlayerBufferExecCompleted(battler); -} - -#define tExpTask_monId data[0] -#define tExpTask_battler data[2] -#define tExpTask_gainedExp_1 data[3] -#define tExpTask_gainedExp_2 data[4] -#define tExpTask_frames data[10] +#define tExpTask_monId data[0] +#define tExpTask_battler data[2] +#define tExpTask_gainedExp_1 data[3] +#define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word. +#define tExpTask_frames data[10] static s32 GetTaskExpValue(u8 taskId) { @@ -1346,7 +1450,7 @@ static void Task_GiveExpToMon(u8 taskId) && (monId == gBattlerPartyIndexes[battler] || monId == gBattlerPartyIndexes[BATTLE_PARTNER(battler)])) gTasks[taskId].func = Task_LaunchLvlUpAnim; else - gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + gTasks[taskId].func = Task_SetControllerToWaitForString; } else { @@ -1457,15 +1561,17 @@ static void Task_UpdateLvlInHealthbox(u8 taskId) if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { u8 monIndex = gTasks[taskId].tExpTask_monId; + if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[BATTLE_PARTNER(battler)]) UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[monIndex], HEALTHBOX_ALL); else UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[monIndex], HEALTHBOX_ALL); - gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + + gTasks[taskId].func = Task_SetControllerToWaitForString; } } -static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId) +static void Task_SetControllerToWaitForString(u8 taskId) { s32 battlerId = gTasks[taskId].tExpTask_battler; @@ -1572,7 +1678,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) caseId = gTasks[gBattleControllerData[battler]].data[0]; DestroyTask(gBattleControllerData[battler]); FreeAllWindowBuffers(); - OpenPartyMenuInTutorialBattle(caseId); + OpenPartyMenuInBattle(caseId); } } @@ -1581,11 +1687,13 @@ static void WaitForMonSelection(u32 battler) if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { if (gPartyMenuUseExitCallback == TRUE) - BtlController_EmitChosenMonReturnValue(battler, 1, gSelectedMonPartyId, gBattlePartyCurrentOrder); + BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, gSelectedMonPartyId, gBattlePartyCurrentOrder); else - BtlController_EmitChosenMonReturnValue(battler, 1, 6, NULL); + BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, PARTY_SIZE, NULL); + if ((gBattleResources->bufferA[battler][1] & 0xF) == 1) PrintLinkStandbyMsg(); + PlayerBufferExecCompleted(battler); } } @@ -1605,7 +1713,43 @@ static void CompleteWhenChoseItem(u32 battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { - BtlController_EmitOneReturnValue(battler, 1, gSpecialVar_ItemId); + BtlController_EmitOneReturnValue(battler, BUFFER_B, gSpecialVar_ItemId); + PlayerBufferExecCompleted(battler); + } +} + +static void PlayerHandleYesNoInput(u32 battler) +{ + if (JOY_NEW(DPAD_UP) && gMultiUsePlayerCursor != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor = 0; + BattleCreateYesNoCursorAt(0); + } + if (JOY_NEW(DPAD_DOWN) && gMultiUsePlayerCursor == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor = 1; + BattleCreateYesNoCursorAt(1); + } + if (JOY_NEW(A_BUTTON)) + { + HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); + PlaySE(SE_SELECT); + + if (gMultiUsePlayerCursor != 0) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 0xE, 0); + else + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 0xD, 0); + + PlayerBufferExecCompleted(battler); + } + if (JOY_NEW(B_BUTTON)) + { + HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); + PlaySE(SE_SELECT); PlayerBufferExecCompleted(battler); } } @@ -1630,7 +1774,7 @@ static void MoveSelectionDisplayMoveNames(u32 battler) } } -static void MoveSelectionDisplayPpString(void) +static void MoveSelectionDisplayPpString(u32 battler) { StringCopy(gDisplayedStringBattle, gText_MoveInterfacePP); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP); @@ -1643,11 +1787,13 @@ static void MoveSelectionDisplayPpNumber(u32 battler) if (gBattleResources->bufferA[battler][2] == TRUE) // check if we didn't want to display pp number return; + SetPpNumbersPaletteInMoveSelection(battler); moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = ConvertIntToDecimalStringN(gDisplayedStringBattle, moveInfo->currentPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); - *txtPtr = CHAR_SLASH; - ConvertIntToDecimalStringN(++txtPtr, moveInfo->maxPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + *(txtPtr)++ = CHAR_SLASH; + ConvertIntToDecimalStringN(txtPtr, moveInfo->maxPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP_REMAINING); } @@ -1658,13 +1804,12 @@ static void MoveSelectionDisplayMoveType(u32 battler) u32 speciesId; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + type = GetMoveType(moveInfo->moves[gMoveSelectionCursor[battler]]); if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST) { if (IsGimmickSelected(battler, GIMMICK_TERA) || GetActiveGimmick(battler) == GIMMICK_TERA) type = GetBattlerTeraType(battler); - end = StringCopy(txtPtr, gTypesInfo[type].name); } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) { @@ -1674,26 +1819,24 @@ static void MoveSelectionDisplayMoveType(u32 battler) || speciesId == SPECIES_OGERPON_HEARTHFLAME || speciesId == SPECIES_OGERPON_HEARTHFLAME_TERA || speciesId == SPECIES_OGERPON_CORNERSTONE || speciesId == SPECIES_OGERPON_CORNERSTONE_TERA) type = gBattleMons[battler].types[1]; - end = StringCopy(txtPtr, gTypesInfo[type].name); } - // Max Guard is a Normal-type move - else if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category == DAMAGE_CATEGORY_STATUS + else if (GetMoveCategory(moveInfo->moves[gMoveSelectionCursor[battler]]) == DAMAGE_CATEGORY_STATUS && (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))) { - type = TYPE_NORMAL; - end = StringCopy(txtPtr, gTypesInfo[type].name); + type = TYPE_NORMAL; // Max Guard is always a Normal-type move } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM) { if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR || (IsGimmickSelected(battler, GIMMICK_TERA) && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL)) type = TYPE_STELLAR; - end = StringCopy(txtPtr, gTypesInfo[type].name); } - else + else if (P_SHOW_DYNAMIC_TYPES) // Non-vanilla changes to battle UI showing dynamic types { - end = StringCopy(txtPtr, gTypesInfo[type].name); + struct Pokemon *mon = &gPlayerParty[gBattlerPartyIndexes[battler]]; + type = CheckDynamicMoveType(mon, moveInfo->moves[gMoveSelectionCursor[battler]], battler); } + end = StringCopy(txtPtr, gTypesInfo[type].name); PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); @@ -1703,9 +1846,8 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) { struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]); u16 move = moveInfo->moves[gMoveSelectionCursor[battler]]; - u16 pwr = gMovesInfo[move].power; - u16 acc = gMovesInfo[move].accuracy; - u8 cat = gMovesInfo[move].category; + u16 pwr = GetMovePower(move); + u16 acc = GetMoveAccuracy(move); u8 pwr_num[3], acc_num[3]; u8 cat_desc[7] = _("CAT: "); @@ -1733,26 +1875,23 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) StringAppend(gDisplayedStringBattle, acc_desc); StringAppend(gDisplayedStringBattle, acc_num); StringAppend(gDisplayedStringBattle, gText_NewLine); - if (gMovesInfo[move].effect == EFFECT_PLACEHOLDER) - StringAppend(gDisplayedStringBattle, gNotDoneYetDescription); - else - StringAppend(gDisplayedStringBattle, gMovesInfo[move].description); + StringAppend(gDisplayedStringBattle, GetMoveDescription(move)); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_DESCRIPTION); if (gCategoryIconSpriteId == 0xFF) gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 38, 64, 1); - StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat); + StartSpriteAnim(&gSprites[gCategoryIconSpriteId], GetBattleMoveCategory(move)); CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL); } -void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum) { u16 src[2]; + src[0] = baseTileNum + 1; + src[1] = baseTileNum + 2; - src[0] = arg1 + 1; - src[1] = arg1 + 2; CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } @@ -1760,19 +1899,19 @@ void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) void MoveSelectionDestroyCursorAt(u8 cursorPosition) { u16 src[2]; - src[0] = 32; src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } -void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum) { u16 src[2]; - src[0] = 1; src[1] = 2; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } @@ -1780,19 +1919,19 @@ void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) void ActionSelectionDestroyCursorAt(u8 cursorPosition) { u16 src[2]; - src[0] = 32; src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } -void SetCB2ToReshowScreenAfterMenu(void) +void CB2_SetUpReshowBattleScreenAfterMenu(void) { SetMainCallback2(ReshowBattleScreenAfterMenu); } -void SetCB2ToReshowScreenAfterMenu2(void) +void CB2_SetUpReshowBattleScreenAfterMenu2(void) { SetMainCallback2(ReshowBattleScreenAfterMenu); } @@ -1848,6 +1987,9 @@ static u32 PlayerGetTrainerBackPicId(void) return trainerPicId; } +// In emerald it's possible to have a tag battle in the battle frontier facilities with AI +// which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) +// that use an animated back pic. static void PlayerHandleDrawTrainerPic(u32 battler) { bool32 isFrontPic; @@ -1922,17 +2064,28 @@ void PlayerHandleBallThrowAnim(u32 battler) static void PlayerHandlePause(u32 battler) { - u8 var = gBattleResources->bufferA[battler][1]; + u8 timer = gBattleResources->bufferA[battler][1]; + + while (timer != 0) + timer--; - while (var) - --var; PlayerBufferExecCompleted(battler); } +static void PlayerHandleMoveAnimation(u32 battler) +{ + BtlController_HandleMoveAnimation(battler); +} + +static void PlayerHandlePrintString(u32 battler) +{ + BtlController_HandlePrintString(battler); +} + static void PlayerHandlePrintSelectionString(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) - BtlController_HandlePrintString(battler); + PlayerHandlePrintString(battler); else PlayerBufferExecCompleted(battler); } @@ -1974,43 +2127,116 @@ static void PlayerHandleChooseAction(u32 battler) ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0); PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]); BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo); - BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); + BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL); + + if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT)) + { + StringCopy(gStringVar1, COMPOUND_STRING("Partner will use:\n")); + u32 move = gBattleMons[B_POSITION_PLAYER_RIGHT].moves[*(gBattleStruct->chosenMovePositions + B_POSITION_PLAYER_RIGHT)]; + StringAppend(gStringVar1, GetMoveName(move)); + u32 moveTarget = GetBattlerMoveTargetType(B_POSITION_PLAYER_RIGHT, move); + if (moveTarget == MOVE_TARGET_SELECTED) + { + if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_LEFT) + StringAppend(gStringVar1, COMPOUND_STRING(" -{UP_ARROW}")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_RIGHT) + StringAppend(gStringVar1, COMPOUND_STRING(" {UP_ARROW}-")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_LEFT) + StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_RIGHT) + StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); + } + else if (moveTarget == MOVE_TARGET_BOTH) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {UP_ARROW}{UP_ARROW}")); + } + else if (moveTarget == MOVE_TARGET_FOES_AND_ALLY) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {V_D_ARROW}{UP_ARROW}")); + } + else if (moveTarget == MOVE_TARGET_ALL_BATTLERS) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {V_D_ARROW}{V_D_ARROW}")); + } + BattlePutTextOnWindow(gStringVar1, B_WIN_ACTION_PROMPT); + } + else + { + BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); + } } -static void HandleChooseMoveAfterDma3(u32 battler) +static void PlayerHandleYesNoBox(u32 battler) +{ + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + { + HandleBattleWindow(YESNOBOX_X_Y, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); + gMultiUsePlayerCursor = 1; + BattleCreateYesNoCursorAt(1); + gBattlerControllerFuncs[battler] = PlayerHandleYesNoInput; + } + else + { + PlayerBufferExecCompleted(battler); + } +} + +void HandleChooseMoveAfterDma3(u32 battler) { if (!IsDma3ManagerBusyWithBgCopy()) { gBattle_BG0_X = 0; - gBattle_BG0_Y = 320; + gBattle_BG0_Y = DISPLAY_HEIGHT * 2; gBattlerControllerFuncs[battler] = HandleInputChooseMove; } } -static void PlayerHandleChooseMove(u32 battler) +// arenaMindPoints is used here as a placeholder for a timer. + +// static void PlayerChooseMoveInBattlePalace(u32 battler) +// { +// if (--*(gBattleStruct->arenaMindPoints + battler) == 0) +// { +// gBattlePalaceMoveSelectionRngValue = gRngValue; +// BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, ChooseMoveAndTargetInBattlePalace(battler)); +// PlayerBufferExecCompleted(battler); +// } +// } + +void PlayerHandleChooseMove(u32 battler) { - struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + // *(gBattleStruct->arenaMindPoints + battler) = 8; + // gBattlerControllerFuncs[battler] = PlayerChooseMoveInBattlePalace; + } + else + { + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); - InitMoveSelectionsVarsAndStrings(battler); - gBattleStruct->gimmick.playerSelect = FALSE; + InitMoveSelectionsVarsAndStrings(battler); + gBattleStruct->gimmick.playerSelect = FALSE; - AssignUsableZMoves(battler, moveInfo->moves); - gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[gMoveSelectionCursor[battler]]) != 0; + AssignUsableZMoves(battler, moveInfo->moves); + gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & (1u << gMoveSelectionCursor[battler])) != 0; - if (!IsGimmickTriggerSpriteActive()) - gBattleStruct->gimmick.triggerSpriteId = 0xFF; - if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) - CreateGimmickTriggerSprite(battler); + if (!IsGimmickTriggerSpriteActive()) + gBattleStruct->gimmick.triggerSpriteId = 0xFF; + if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) + CreateGimmickTriggerSprite(battler); - gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3; + gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3; + } } void InitMoveSelectionsVarsAndStrings(u32 battler) { + LoadTypeIcons(battler); MoveSelectionDisplayMoveNames(battler); gMultiUsePlayerCursor = 0xFF; MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); } @@ -2034,14 +2260,23 @@ static void PlayerHandleChoosePokemon(u32 battler) for (i = 0; i < ARRAY_COUNT(gBattlePartyCurrentOrder); i++) gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][4 + i]; - gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF); - gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF; - *(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4; - *(&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; - gBattlerInMenuId = battler; + if (gBattleTypeFlags & BATTLE_TYPE_ARENA && (gBattleResources->bufferA[battler][1] & 0xF) != PARTY_ACTION_CANT_SWITCH + && (gBattleResources->bufferA[battler][1] & 0xF) != PARTY_ACTION_CHOOSE_FAINTED_MON) + { + // BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, gBattlerPartyIndexes[battler] + 1, gBattlePartyCurrentOrder); + // PlayerBufferExecCompleted(battler); + } + else + { + gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF); + gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF; + *(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4; + *(&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; + gBattlerInMenuId = battler; + } } static void PlayerHandleCmd23(u32 battler) @@ -2056,7 +2291,6 @@ static void PlayerHandleHealthBarUpdate(u32 battler) BtlController_HandleHealthBarUpdate(battler, TRUE); } -// consider moving to battle_controller, shared with battle_controller_pokedude void PlayerHandleExpUpdate(u32 battler) { u8 monId = gBattleResources->bufferA[battler][1]; @@ -2096,16 +2330,16 @@ static void PlayerHandleStatusXor(u32 battler) static void PlayerHandleDMA3Transfer(u32 battler) { u32 dstArg = gBattleResources->bufferA[battler][1] - | (gBattleResources->bufferA[battler][2] << 8) - | (gBattleResources->bufferA[battler][3] << 16) - | (gBattleResources->bufferA[battler][4] << 24); + | (gBattleResources->bufferA[battler][2] << 8) + | (gBattleResources->bufferA[battler][3] << 16) + | (gBattleResources->bufferA[battler][4] << 24); u16 sizeArg = gBattleResources->bufferA[battler][5] | (gBattleResources->bufferA[battler][6] << 8); const u8 *src = &gBattleResources->bufferA[battler][7]; u8 *dst = (u8 *)(dstArg); u32 size = sizeArg; - while (TRUE) + while (1) { if (size <= 0x1000) { @@ -2156,19 +2390,8 @@ static void PlayerHandleIntroTrainerBallThrow(u32 battler) BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F8, trainerPal, 31, Intro_TryShinyAnimShowHealthbox, PlayerThrowBall_StartAnimLinearTranslation); } -void SpriteCB_FreePlayerSpriteLoadMonSpriteOld(struct Sprite *sprite) -{ - u8 battlerId = sprite->data[5]; - - FreeSpriteOamMatrix(sprite); - FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum)); - DestroySprite(sprite); - BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); - StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], 0); -} - static void PlayerHandleDrawPartyStatusSummary(u32 battler) -{ +{ BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } @@ -2223,7 +2446,9 @@ static void PlayerHandleResetActionMoveSelection(u32 battler) static void PlayerHandleEndLinkBattle(u32 battler) { + // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); gBattleOutcome = gBattleResources->bufferA[battler][1]; + // gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; FadeOutMapMusic(5); BeginFastPaletteFade(3); PlayerBufferExecCompleted(battler); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index f5775beed..de3dd0659 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -70,7 +70,7 @@ static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 bat [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = PlayerPartnerHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = PlayerPartnerHandleChooseMove, [CONTROLLER_OPENBAG] = BtlController_Empty, [CONTROLLER_CHOOSEPOKEMON] = PlayerPartnerHandleChoosePokemon, diff --git a/src/battle_controller_pokedude.c b/src/battle_controller_pokedude.c index 850ccc269..9eb6ff936 100644 --- a/src/battle_controller_pokedude.c +++ b/src/battle_controller_pokedude.c @@ -95,7 +95,7 @@ static void (*const sPokedudeBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = PokedudeHandlePrintSelectionString, // done [CONTROLLER_CHOOSEACTION] = PokedudeHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = PokedudeHandleChooseMove, // done [CONTROLLER_OPENBAG] = PokedudeHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = PokedudeHandleChoosePokemon, // done diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index da36608cf..c3450e63b 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -74,7 +74,7 @@ static void (*const sRecordedOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 [CONTROLLER_PRINTSTRING] = RecordedOpponentHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = RecordedOpponentHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = RecordedOpponentHandleChooseMove, [CONTROLLER_OPENBAG] = RecordedOpponentHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = RecordedOpponentHandleChoosePokemon, diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index e49a9e7e8..2852b30c8 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -72,7 +72,7 @@ static void (*const sRecordedPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 ba [CONTROLLER_PRINTSTRING] = RecordedPlayerHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = RecordedPlayerHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = RecordedPlayerHandleChooseMove, [CONTROLLER_OPENBAG] = RecordedPlayerHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = RecordedPlayerHandleChoosePokemon, diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c index 141cfe7dd..24488746f 100644 --- a/src/battle_controller_safari.c +++ b/src/battle_controller_safari.c @@ -52,7 +52,7 @@ static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = SafariHandlePrintSelectionString, // done [CONTROLLER_CHOOSEACTION] = SafariHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = BtlController_Empty, // done [CONTROLLER_OPENBAG] = SafariHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty, // done diff --git a/src/data/items.h b/src/data/items.h index e8531ab06..9a7110891 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -2052,6 +2052,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "awakens\nsleeping " "Pokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_Medicine, @@ -2070,6 +2071,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "snaps Pokémon\n" "out of confusion."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -2088,6 +2090,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "snaps Pokémon\n" "out of attraction."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -2109,6 +2112,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "keeps away\nwild " "Pokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_BlackWhiteFlute, @@ -2125,6 +2129,7 @@ const struct Item gItemsInfo[] = .description = COMPOUND_STRING( "A glass flute that " "lures wild\nPokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_BlackWhiteFlute, diff --git a/src/graphics.c b/src/graphics.c index 3fc34e902..cf9a8a587 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1937,3 +1937,8 @@ const u32 gBattleAnimBgPalette_ZMoveMountain[] = INCBIN_U32("graphics/battle_ani const u32 gBattleAnimBgTilemap_ZMoveMountain[] = INCBIN_U32("graphics/battle_anims/backgrounds/zmove_mountain.bin.lz"); const u32 gBattleAnimBgPalette_SteelBeam[] = INCBIN_U32("graphics/battle_anims/backgrounds/steel_beam.gbapal.lz"); + +const u32 gBattleIcons_Gfx1[] = INCBIN_U32("graphics/types/battle_icons1.4bpp.lz"); +const u32 gBattleIcons_Gfx2[] = INCBIN_U32("graphics/types/battle_icons2.4bpp.lz"); +const u32 gBattleIcons_Pal1[] = INCBIN_U32("graphics/types/battle_icons1.gbapal.lz"); +const u32 gBattleIcons_Pal2[] = INCBIN_U32("graphics/types/battle_icons2.gbapal.lz"); diff --git a/src/item.c b/src/item.c index 337309a23..8b86828b3 100644 --- a/src/item.c +++ b/src/item.c @@ -662,6 +662,11 @@ u8 ItemId_GetImportance(u16 itemId) return gItemsInfo[SanitizeItemId(itemId)].importance; } +u8 ItemId_GetConsumability(u16 itemId) +{ + return !gItemsInfo[SanitizeItemId(itemId)].notConsumed; +} + u8 ItemId_GetPocket(u16 itemId) { return gItemsInfo[SanitizeItemId(itemId)].pocket; diff --git a/src/item_menu.c b/src/item_menu.c index 29fd9f057..7c566ba28 100644 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -351,7 +351,7 @@ void CB2_BagMenuFromStartMenu(void) void CB2_BagMenuFromBattle(void) { - GoToBagMenu(ITEMMENULOCATION_BATTLE, OPEN_BAG_LAST, SetCB2ToReshowScreenAfterMenu2); + GoToBagMenu(ITEMMENULOCATION_BATTLE, OPEN_BAG_LAST, CB2_SetUpReshowBattleScreenAfterMenu2); } static void CB2_BagMenuRun(void) @@ -949,7 +949,7 @@ void Bag_BeginCloseWin0Animation(void) gBagMenuState.bagOpen = FALSE; } -void CB2_SetUpReshowBattleScreenAfterMenu(void) +void SetBagOpenFalse(void) { gBagMenuState.bagOpen = FALSE; } @@ -2101,7 +2101,7 @@ void InitOldManBag(void) BackUpPlayerBag(); AddBagItem(ITEM_POTION, 1); AddBagItem(ITEM_POKE_BALL, 1); - GoToBagMenu(ITEMMENULOCATION_OLD_MAN, OPEN_BAG_ITEMS, SetCB2ToReshowScreenAfterMenu2); + GoToBagMenu(ITEMMENULOCATION_OLD_MAN, OPEN_BAG_ITEMS, CB2_SetUpReshowBattleScreenAfterMenu2); } #define tFrameCounter data[8] @@ -2183,11 +2183,11 @@ void InitPokedudeBag(u8 a0) location = a0; break; case ITEMMENULOCATION_TTVSCR_STATUS: - cb2 = SetCB2ToReshowScreenAfterMenu2; + cb2 = CB2_SetUpReshowBattleScreenAfterMenu2; location = ITEMMENULOCATION_TTVSCR_STATUS; break; case ITEMMENULOCATION_TTVSCR_CATCHING: - cb2 = SetCB2ToReshowScreenAfterMenu2; + cb2 = CB2_SetUpReshowBattleScreenAfterMenu2; location = ITEMMENULOCATION_TTVSCR_CATCHING; break; } diff --git a/src/line_break.c b/src/line_break.c new file mode 100644 index 000000000..b8888f501 --- /dev/null +++ b/src/line_break.c @@ -0,0 +1,281 @@ +#include "global.h" +#include "line_break.h" +#include "text.h" +#include "malloc.h" + +void StripLineBreaks(u8 *src) +{ + u32 currIndex = 0; + while (src[currIndex] != EOS) + { + if (src[currIndex] == CHAR_PROMPT_SCROLL || src[currIndex] == CHAR_NEWLINE) + src[currIndex] = CHAR_SPACE; + currIndex++; + } +} + +void BreakStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId) +{ + u32 currIndex = 0; + u8 *currSrc = src; + while (src[currIndex] != EOS) + { + if (src[currIndex] == CHAR_PROMPT_CLEAR) + { + u8 replacedChar = src[currIndex + 1]; + src[currIndex + 1] = EOS; + BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId); + src[currIndex + 1] = replacedChar; + currSrc = &src[currIndex + 1]; + } + currIndex++; + } + BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId); +} + +void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId) +{ + // If the string already has line breaks, don't interfere with them + if (StringHasManualBreaks(src)) + return; + // Sanity check + if (src[0] == EOS) + return; + u32 numChars = 1; + u32 numWords = 1; + u32 currWordIndex = 0; + u32 currWordLength = 1; + bool32 isPrevCharSplitting = FALSE; + bool32 isCurrCharSplitting; + // Get numbers of chars in string and count words + while (src[numChars] != EOS) + { + isCurrCharSplitting = IsWordSplittingChar(src, numChars); + if (isCurrCharSplitting && !isPrevCharSplitting) + numWords++; + isPrevCharSplitting = isCurrCharSplitting; + numChars++; + } + // Allocate enough space for word data + struct StringWord *allWords = Alloc(numWords*sizeof(struct StringWord)); + + allWords[currWordIndex].startIndex = 0; + allWords[currWordIndex].width = 0; + isPrevCharSplitting = FALSE; + // Fill in word begin index and lengths + for (u32 i = 1; i < numChars; i++) + { + isCurrCharSplitting = IsWordSplittingChar(src, i); + if (isCurrCharSplitting && !isPrevCharSplitting) + { + allWords[currWordIndex].length = currWordLength; + currWordIndex++; + currWordLength = 0; + } + else if (!isCurrCharSplitting && isPrevCharSplitting) + { + allWords[currWordIndex].startIndex = i; + allWords[currWordIndex].width = 0; + currWordLength++; + } + else + { + currWordLength++; + } + isPrevCharSplitting = isCurrCharSplitting; + } + allWords[currWordIndex].length = currWordLength; + + // Fill in individual word widths + for (u32 i = 0; i < numWords; i++) + { + for (u32 j = 0; j < allWords[i].length; j++) + allWords[i].width += GetGlyphWidth(src[allWords[i].startIndex + j], FALSE, fontId); + } + + // Step 1: Does it all fit one one line? Then no break + // Step 2: Try to split across minimum number of lines + u32 spaceWidth = GetGlyphWidth(0, FALSE, fontId); + u32 totalWidth = allWords[0].width; + // Calculate total widths without any line breaks + for (u32 i = 1; i < numWords; i++) + totalWidth += allWords[i].width + spaceWidth; + + // If it doesn't fit on 1 line, do fancy line break calculation + // NOTE: Currently the line break calculation isn't fancy + if (totalWidth > maxWidth) + { + // Figure out how many lines are needed with naive method + u32 currLineWidth = 0; + u32 totalLines = 1; + bool32 shouldTryAgain; + for (currWordIndex = 0; currWordIndex < numWords; currWordIndex++) + { + if (currLineWidth + allWords[currWordIndex].length > maxWidth) + { + totalLines++; + currLineWidth = allWords[currWordIndex].width; + } + else + { + currLineWidth += allWords[currWordIndex].width + spaceWidth; + } + } + // LINE LAYOUT STARTS HERE + struct StringLine *stringLines; + do + { + shouldTryAgain = FALSE; + u16 targetLineWidth = totalWidth/totalLines; + stringLines = Alloc(totalLines*sizeof(struct StringLine)); + for (u32 lineIndex = 0; lineIndex < totalLines; lineIndex++) + { + stringLines[lineIndex].numWords = 0; + stringLines[lineIndex].spaceWidth = spaceWidth; + stringLines[lineIndex].extraSpaceWidth = 0; + } + currWordIndex = 0; + u16 currLineIndex = 0; + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + while (currWordIndex < numWords) + { + if (currLineWidth + spaceWidth + allWords[currWordIndex].width > maxWidth) + { + // go to next line + currLineIndex++; + if (currLineIndex == totalLines) + { + totalLines++; + Free(stringLines); + shouldTryAgain = TRUE; + break; + } + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + } + else if (currLineWidth > targetLineWidth) + { + // go to next line + currLineIndex++; + if (currLineIndex == totalLines) + { + totalLines++; + Free(stringLines); + shouldTryAgain = TRUE; + break; + } + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + } + else + { + // continue on current line + // add word and space width + currLineWidth += spaceWidth + allWords[currWordIndex].width; + stringLines[currLineIndex].numWords++; + currWordIndex++; + } + } + } while (shouldTryAgain); + //u32 currBadness = GetStringBadness(stringLines, totalLines, maxWidth); + BuildNewString(stringLines, totalLines, screenLines, src); + Free(stringLines); + } + + Free(allWords); +} + +// Only allow word splitting on allowed chars +bool32 IsWordSplittingChar(const u8 *src, u32 index) +{ + switch (src[index]) + { + case CHAR_SPACE: + return TRUE; + default: + return FALSE; + } +} + +// Badness calculation +// unfilled lines scale linerarly +// jagged lines scales by the square +// runts scale linearly +// numbers not final +// ISN'T ACTUALLY USED RIGHT NOW +u32 GetStringBadness(struct StringLine *stringLines, u32 numLines, u32 maxWidth) +{ + u32 badness = 0; + u32 *lineWidths = Alloc(numLines*4); + u32 widestWidth = 0; + for (u32 i = 0; i < numLines; i++) + { + lineWidths[i] = 0; + for (u32 j = 0; j < stringLines[i].numWords; j++) + lineWidths[i] += stringLines[i].words[j].width; + lineWidths[i] += (stringLines[i].numWords-1)*stringLines[i].spaceWidth; + if (lineWidths[i] > widestWidth) + widestWidth = lineWidths[i]; + if (stringLines[i].numWords == 1) + badness += BADNESS_RUNT; + } + for (u32 i = 0; i < numLines; i++) + { + u32 extraSpaceWidth = 0; + if (lineWidths[i] != widestWidth) + { + // Not the best way to do this, ideally a line should be allowed to get longer than current widest + // line. But then the widest line has to be recalculated. + while (lineWidths[i] + (extraSpaceWidth + 1) * (stringLines[i].numWords - 1) < widestWidth && extraSpaceWidth < MAX_SPACE_WIDTH) + extraSpaceWidth++; + lineWidths[i] += extraSpaceWidth*(stringLines[i].numWords-1); + } + badness += (maxWidth - lineWidths[i]) * BADNESS_UNFILLED; + u32 baseBadness = (widestWidth - lineWidths[i]) * BADNESS_JAGGED; + badness += baseBadness*baseBadness; + stringLines[i].extraSpaceWidth = extraSpaceWidth; + } + Free(lineWidths); + return badness; +} + +// Build the new string from the data stored in the StringLine structs +void BuildNewString(struct StringLine *stringLines, u32 numLines, u32 maxLines, u8 *str) +{ + u32 srcCharIndex = 0; + for (u32 lineIndex = 0; lineIndex < numLines; lineIndex++) + { + srcCharIndex += stringLines[lineIndex].words[0].length; + for (u32 wordIndex = 1; wordIndex < stringLines[lineIndex].numWords; wordIndex++) + // Add length of word and a space + srcCharIndex += stringLines[lineIndex].words[wordIndex].length + 1; + if (lineIndex + 1 < numLines) + { + // Add the appropriate line break depending on line number + if (lineIndex >= maxLines - 1 && numLines > maxLines) + str[srcCharIndex] = CHAR_PROMPT_SCROLL; + else + str[srcCharIndex] = CHAR_NEWLINE; + srcCharIndex++; + } + } +} + +bool32 StringHasManualBreaks(u8 *src) +{ + u32 charIndex = 0; + while (src[charIndex] != EOS) + { + if (src[charIndex] == CHAR_PROMPT_SCROLL || src[charIndex] == CHAR_NEWLINE) + return TRUE; + charIndex++; + } + return FALSE; +} diff --git a/src/party_menu.c b/src/party_menu.c index e11ddbf3a..3e8c277f6 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6562,7 +6562,7 @@ static u8 GetPartyLayoutFromBattleType(void) return PARTY_LAYOUT_DOUBLE; } -void OpenPartyMenuInTutorialBattle(u8 partyAction) +void OpenPartyMenuInBattle(u8 partyAction) { if (!BtlCtrl_OakOldMan_TestState2Flag(FIRST_BATTLE_MSG_FLAG_PARTY_MENU) && (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)) { @@ -6572,7 +6572,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) FALSE, PARTY_MSG_NONE, Task_FirstBattleEnterParty_WaitFadeIn, - SetCB2ToReshowScreenAfterMenu); + CB2_SetUpReshowBattleScreenAfterMenu); BtlCtrl_OakOldMan_SetState2Flag(FIRST_BATTLE_MSG_FLAG_PARTY_MENU); } else @@ -6583,7 +6583,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, - SetCB2ToReshowScreenAfterMenu); + CB2_SetUpReshowBattleScreenAfterMenu); } ReshowBattleScreenDummy(); UpdatePartyToBattleOrder(); @@ -6591,7 +6591,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) void Pokedude_OpenPartyMenuInBattle(void) { - InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_PartyMenu_Pokedude, SetCB2ToReshowScreenAfterMenu); + InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_PartyMenu_Pokedude, CB2_SetUpReshowBattleScreenAfterMenu); ReshowBattleScreenDummy(); UpdatePartyToBattleOrder(); } @@ -6948,8 +6948,8 @@ static void UpdatePartyToFieldOrder(void) static void CB2_SetUpExitToBattleScreen(void) { - CB2_SetUpReshowBattleScreenAfterMenu(); - SetMainCallback2(SetCB2ToReshowScreenAfterMenu); + SetBagOpenFalse(); + SetMainCallback2(CB2_SetUpReshowBattleScreenAfterMenu); } void ShowPartyMenuToShowcaseMultiBattleParty(void) diff --git a/src/pokemon.c b/src/pokemon.c index 46000d437..dfc4be449 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6561,3 +6561,11 @@ void UpdateDaysPassedSinceFormChange(u16 days) } } } + +u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler) +{ + u32 moveType = GetDynamicMoveType(mon, move, battler, NULL); + if (moveType != TYPE_NONE) + return moveType; + return GetMoveType(move); +} diff --git a/src/tm_case.c b/src/tm_case.c index 2ebe80203..3a6f384de 100644 --- a/src/tm_case.c +++ b/src/tm_case.c @@ -1466,7 +1466,7 @@ static void Task_Pokedude_Run(u8 taskId) sTMCaseStaticResources.scrollOffset = sPokedudeBagBackup->scrollOffset; Free(sPokedudeBagBackup); CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_SIZE); - CB2_SetUpReshowBattleScreenAfterMenu(); + SetBagOpenFalse(); BeginNormalPaletteFade(PALETTES_ALL, -2, 0, 16, 0); tPokedudeState++; } diff --git a/src/type_icons.c b/src/type_icons.c new file mode 100644 index 000000000..bd940ec84 --- /dev/null +++ b/src/type_icons.c @@ -0,0 +1,552 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_gimmick.h" +#include "decompress.h" +#include "graphics.h" +#include "pokedex.h" +#include "sprite.h" +#include "type_icons.h" + +static void LoadTypeSpritesAndPalettes(void); +static void LoadTypeIconsPerBattler(u32, u32); + +static bool32 UseDoubleBattleCoords(u32); + +static u32 GetMonPublicType(u32, u32); +static bool32 ShouldHideUncaughtType(u32); +static u32 GetMonDefensiveTeraType(struct Pokemon *, struct Pokemon*, u32, u32, u32, u32); +static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon*, u32, u32); + +static void CreateSpriteFromType(u32, bool32, u32[], u32, u32); +static bool32 ShouldSkipSecondType(u32[], u32); +static void SetTypeIconXY(s32*, s32*, u32, bool32, u32); + +static void CreateSpriteAndSetTypeSpriteAttributes(u32, u32 x, u32 y, u32, u32, bool32); +static bool32 ShouldFlipTypeIcon(bool32, u32, u32); + +static void SpriteCB_TypeIcon(struct Sprite*); +static void DestroyTypeIcon(struct Sprite*); +static void FreeAllTypeIconResources(void); +static bool32 ShouldHideTypeIcon(u32); +static s32 GetTypeIconHideMovement(bool32, u32); +static s32 GetTypeIconSlideMovement(bool32, u32, s32); +static s32 GetTypeIconBounceMovement(s32, u32); + +const struct Coords16 sTypeIconPositions[][2] = +{ + [B_POSITION_PLAYER_LEFT] = + { + [FALSE] = {221, 86}, + [TRUE] = {144, 71}, + }, + [B_POSITION_OPPONENT_LEFT] = + { + [FALSE] = {20, 26}, + [TRUE] = {97, 14}, + }, + [B_POSITION_PLAYER_RIGHT] = + { + [TRUE] = {156, 96}, + }, + [B_POSITION_OPPONENT_RIGHT] = + { + [TRUE] = {85, 39}, + }, +}; + +const union AnimCmd sSpriteAnim_TypeIcon_Normal[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_NORMAL), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Fighting[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_FIGHTING), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Flying[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_FLYING), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Poison[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_POISON), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ground[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_GROUND), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Rock[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_ROCK), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Bug[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_BUG), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ghost[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_GHOST), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Steel[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_STEEL), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Mystery[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_MYSTERY), 0), + ANIMCMD_END +}; + +const union AnimCmd sSpriteAnim_TypeIcon_Fire[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_FIRE), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Water[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_WATER), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Grass[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_GRASS), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Electric[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_ELECTRIC), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Psychic[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_PSYCHIC), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ice[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_ICE), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Dragon[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_DRAGON), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Dark[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_DARK), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Fairy[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_FAIRY), 0), + ANIMCMD_END +}; + +const union AnimCmd *const sSpriteAnimTable_TypeIcons[] = +{ + [TYPE_NONE] = sSpriteAnim_TypeIcon_Mystery, + [TYPE_NORMAL] = sSpriteAnim_TypeIcon_Normal, + [TYPE_FIGHTING] = sSpriteAnim_TypeIcon_Fighting, + [TYPE_FLYING] = sSpriteAnim_TypeIcon_Flying, + [TYPE_POISON] = sSpriteAnim_TypeIcon_Poison, + [TYPE_GROUND] = sSpriteAnim_TypeIcon_Ground, + [TYPE_ROCK] = sSpriteAnim_TypeIcon_Rock, + [TYPE_BUG] = sSpriteAnim_TypeIcon_Bug, + [TYPE_GHOST] = sSpriteAnim_TypeIcon_Ghost, + [TYPE_STEEL] = sSpriteAnim_TypeIcon_Steel, + [TYPE_MYSTERY] = sSpriteAnim_TypeIcon_Mystery, + [TYPE_FIRE] = sSpriteAnim_TypeIcon_Fire, + [TYPE_WATER] = sSpriteAnim_TypeIcon_Water, + [TYPE_GRASS] = sSpriteAnim_TypeIcon_Grass, + [TYPE_ELECTRIC] = sSpriteAnim_TypeIcon_Electric, + [TYPE_PSYCHIC] = sSpriteAnim_TypeIcon_Psychic, + [TYPE_ICE] = sSpriteAnim_TypeIcon_Ice, + [TYPE_DRAGON] = sSpriteAnim_TypeIcon_Dragon, + [TYPE_DARK] = sSpriteAnim_TypeIcon_Dark, + [TYPE_FAIRY] = sSpriteAnim_TypeIcon_Fairy, + [TYPE_STELLAR] = sSpriteAnim_TypeIcon_Mystery, +}; + +const struct CompressedSpritePalette sTypeIconPal1 = +{ + .data = gBattleIcons_Pal1, + .tag = TYPE_ICON_TAG +}; + +const struct CompressedSpritePalette sTypeIconPal2 = +{ + .data = gBattleIcons_Pal2, + .tag = TYPE_ICON_TAG_2 +}; + +const struct OamData sOamData_TypeIcons = +{ + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .shape = SPRITE_SHAPE(8x16), + .size = SPRITE_SIZE(8x16), + .priority = 1, +}; + +const struct CompressedSpriteSheet sSpriteSheet_TypeIcons2 = +{ + .data = gBattleIcons_Gfx2, + .size = (8*16) * 9, + .tag = TYPE_ICON_TAG_2, +}; + +const struct CompressedSpriteSheet sSpriteSheet_TypeIcons1 = +{ + .data = gBattleIcons_Gfx1, + .size = (8*16) * 10, + .tag = TYPE_ICON_TAG, +}; + +const struct SpriteTemplate sSpriteTemplate_TypeIcons1 = +{ + .tileTag = TYPE_ICON_TAG, + .paletteTag = TYPE_ICON_TAG, + .oam = &sOamData_TypeIcons, + .anims = sSpriteAnimTable_TypeIcons, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TypeIcon +}; + +const struct SpriteTemplate sSpriteTemplate_TypeIcons2 = +{ + .tileTag = TYPE_ICON_TAG_2, + .paletteTag = TYPE_ICON_TAG_2, + .oam = &sOamData_TypeIcons, + .anims = sSpriteAnimTable_TypeIcons, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TypeIcon +}; + +void LoadTypeIcons(u32 battler) +{ + u32 position; + + if (B_SHOW_TYPES == SHOW_TYPES_NEVER) + return; + + LoadTypeSpritesAndPalettes(); + + for (position = 0; position < gBattlersCount; ++position) + LoadTypeIconsPerBattler(battler, position); +} + +static void LoadTypeSpritesAndPalettes(void) +{ + if (IndexOfSpritePaletteTag(TYPE_ICON_TAG) != UCHAR_MAX) + return; + + LoadCompressedSpriteSheet(&sSpriteSheet_TypeIcons1); + LoadCompressedSpriteSheet(&sSpriteSheet_TypeIcons2); + LoadCompressedSpritePalette(&sTypeIconPal1); + LoadCompressedSpritePalette(&sTypeIconPal2); +} + +static void LoadTypeIconsPerBattler(u32 battler, u32 position) +{ + u32 typeNum, types[2]; + u32 battlerId = GetBattlerAtPosition(position); + bool32 useDoubleBattleCoords = UseDoubleBattleCoords(battlerId); + + if (!IsBattlerAlive(battlerId)) + return; + + for (typeNum = 0; typeNum < 2; ++typeNum) + types[typeNum] = GetMonPublicType(battlerId, typeNum); + + for (typeNum = 0; typeNum < 2; ++typeNum) + CreateSpriteFromType(position, useDoubleBattleCoords, types, typeNum, battler); +} + +static bool32 UseDoubleBattleCoords(u32 position) +{ + if (!IsDoubleBattle()) + return FALSE; + + if ((position == B_POSITION_PLAYER_LEFT) && (gBattleMons[B_POSITION_PLAYER_RIGHT].species == SPECIES_NONE)) + return FALSE; + + if ((position == B_POSITION_OPPONENT_LEFT) && (gBattleMons[B_POSITION_OPPONENT_RIGHT].species == SPECIES_NONE)) + return FALSE; + + return TRUE; +} + +static u32 GetMonPublicType(u32 battlerId, u32 typeNum) +{ + struct Pokemon* mon = GetPartyBattlerData(battlerId); + u32 monSpecies = GetMonData(mon,MON_DATA_SPECIES,NULL); + struct Pokemon* monIllusion; + u32 illusionSpecies; + + if (ShouldHideUncaughtType(monSpecies)) + return TYPE_MYSTERY; + + monIllusion = GetIllusionMonPtr(battlerId); + illusionSpecies = GetMonData(monIllusion,MON_DATA_SPECIES,NULL); + + if (GetActiveGimmick(battlerId) == GIMMICK_TERA) + return GetMonDefensiveTeraType(mon,monIllusion,battlerId,typeNum,illusionSpecies,monSpecies); + + if (IsIllusionActiveAndTypeUnchanged(monIllusion,monSpecies, battlerId)) + return gSpeciesInfo[illusionSpecies].types[typeNum]; + + return gBattleMons[battlerId].types[typeNum]; +} + +static bool32 ShouldHideUncaughtType(u32 species) +{ + if (B_SHOW_TYPES != SHOW_TYPES_CAUGHT) + return FALSE; + + if (GetSetPokedexFlag(SpeciesToNationalDexNum(species),FLAG_GET_CAUGHT)) + return FALSE; + + return TRUE; +} + +static u32 GetMonDefensiveTeraType(struct Pokemon * mon, struct Pokemon* monIllusion, u32 battlerId, u32 typeNum, u32 illusionSpecies, u32 monSpecies) +{ + u32 teraType = GetBattlerTeraType(battlerId); + u32 targetSpecies; + + if (teraType != TYPE_STELLAR) + return teraType; + + targetSpecies = (monIllusion != NULL) ? illusionSpecies : monSpecies; + + return gSpeciesInfo[targetSpecies].types[typeNum]; +} + +static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon* monIllusion, u32 monSpecies, u32 battlerId) +{ + u32 typeNum; + + if (monIllusion == NULL) + return FALSE; + + for (typeNum = 0; typeNum < 2; typeNum++) + if (gSpeciesInfo[monSpecies].types[typeNum] != gBattleMons[battlerId].types[typeNum]) + return FALSE; + + return TRUE; +} + +static void CreateSpriteFromType(u32 position, bool32 useDoubleBattleCoords, u32 types[], u32 typeNum, u32 battler) +{ + s32 x = 0, y = 0; + + if (ShouldSkipSecondType(types, typeNum)) + return; + + SetTypeIconXY(&x, &y, position, useDoubleBattleCoords, typeNum); + + CreateSpriteAndSetTypeSpriteAttributes(types[typeNum], x, y, position, battler, useDoubleBattleCoords); +} + +static bool32 ShouldSkipSecondType(u32 types[], u32 typeNum) +{ + if (!typeNum) + return FALSE; + + if (types[0] != types[1]) + return FALSE; + + return TRUE; +} + +static void SetTypeIconXY(s32* x, s32* y, u32 position, bool32 useDoubleBattleCoords, u32 typeNum) +{ + *x = sTypeIconPositions[position][useDoubleBattleCoords].x; + *y = sTypeIconPositions[position][useDoubleBattleCoords].y + (11 * typeNum); +} + +static void CreateSpriteAndSetTypeSpriteAttributes(u32 type, u32 x, u32 y, u32 position, u32 battler, bool32 useDoubleBattleCoords) +{ + struct Sprite* sprite; + const struct SpriteTemplate* spriteTemplate = gTypesInfo[type].useSecondTypeIconPalette ? &sSpriteTemplate_TypeIcons2 : &sSpriteTemplate_TypeIcons1; + u32 spriteId = CreateSpriteAtEnd(spriteTemplate, x, y, UCHAR_MAX); + + if (spriteId == MAX_SPRITES) + return; + + sprite = &gSprites[spriteId]; + sprite->tMonPosition = position; + sprite->tBattlerId = battler; + sprite->tVerticalPosition = y; + + sprite->hFlip = ShouldFlipTypeIcon(useDoubleBattleCoords, position, type); + + StartSpriteAnim(sprite, type); +} + +static bool32 ShouldFlipTypeIcon(bool32 useDoubleBattleCoords, u32 position, u32 typeId) +{ + bool32 side = (useDoubleBattleCoords) ? B_SIDE_OPPONENT : B_SIDE_PLAYER; + + if (GetBattlerSide(GetBattlerAtPosition(position)) != side) + return FALSE; + + return !gTypesInfo[typeId].isSpecialCaseType; +} + +static void SpriteCB_TypeIcon(struct Sprite* sprite) +{ + u32 position = sprite->tMonPosition; + u32 battlerId = sprite->tBattlerId; + bool32 useDoubleBattleCoords = UseDoubleBattleCoords(GetBattlerAtPosition(position)); + + if (sprite->tHideIconTimer == NUM_FRAMES_HIDE_TYPE_ICON) + { + DestroyTypeIcon(sprite); + return; + } + + if (ShouldHideTypeIcon(battlerId)) + { + sprite->x += GetTypeIconHideMovement(useDoubleBattleCoords, position); + ++sprite->tHideIconTimer; + return; + } + + sprite->x += GetTypeIconSlideMovement(useDoubleBattleCoords,position, sprite->x); + sprite->y = GetTypeIconBounceMovement(sprite->tVerticalPosition,position); +} + +static const u32 typeIconTags[] = +{ + TYPE_ICON_TAG, + TYPE_ICON_TAG_2 +}; + +static void DestroyTypeIcon(struct Sprite* sprite) +{ + u32 spriteId, tag; + + DestroySpriteAndFreeResources(sprite); + + for (spriteId = 0; spriteId < MAX_SPRITES; ++spriteId) + { + if (!gSprites[spriteId].inUse) + continue; + + for (tag = 0; tag < 2; tag++) + { + if (gSprites[spriteId].template->paletteTag == typeIconTags[tag]) + return; + + if (gSprites[spriteId].template->tileTag == typeIconTags[tag]) + return; + } + } + + FreeAllTypeIconResources(); +} + +static void FreeAllTypeIconResources(void) +{ + u32 tag; + + for (tag = 0; tag < 2; tag++) + { + FreeSpriteTilesByTag(typeIconTags[tag]); + FreeSpritePaletteByTag(typeIconTags[tag]); + } +} + +static void (* const sShowTypesControllerFuncs[])(u32 battler) = +{ + PlayerHandleChooseMove, + HandleChooseMoveAfterDma3, + HandleInputChooseTarget, + HandleInputShowTargets, + HandleInputShowEntireFieldTargets, + HandleMoveSwitching, + HandleInputChooseMove, +}; + + +static bool32 ShouldHideTypeIcon(u32 battlerId) +{ + u32 funcIndex; + + for (funcIndex = 0; funcIndex < ARRAY_COUNT(sShowTypesControllerFuncs); funcIndex++) + if (gBattlerControllerFuncs[battlerId] == sShowTypesControllerFuncs[funcIndex]) + return FALSE; + + return TRUE; +} + +static s32 GetTypeIconHideMovement(bool32 useDoubleBattleCoords, u32 position) +{ + if (useDoubleBattleCoords) + { + if (position == B_POSITION_PLAYER_LEFT || position == B_POSITION_PLAYER_RIGHT) + return 1; + else + return -1; + } + + if (position == B_POSITION_PLAYER_LEFT) + return -1; + else + return 1; +} + +static s32 GetTypeIconSlideMovement(bool32 useDoubleBattleCoords, u32 position, s32 xPos) +{ + if (useDoubleBattleCoords) + { + switch (position) + { + case B_POSITION_PLAYER_LEFT: + case B_POSITION_PLAYER_RIGHT: + if (xPos > sTypeIconPositions[position][useDoubleBattleCoords].x - 10) + return -1; + break; + default: + case B_POSITION_OPPONENT_LEFT: + case B_POSITION_OPPONENT_RIGHT: + if (xPos < sTypeIconPositions[position][useDoubleBattleCoords].x + 10) + return 1; + break; + } + return 0; + } + + if (position == B_POSITION_PLAYER_LEFT) + { + if (xPos < sTypeIconPositions[position][useDoubleBattleCoords].x + 10) + return 1; + } + else + { + if (xPos > sTypeIconPositions[position][useDoubleBattleCoords].x - 10) + return -1; + } + return 0; +} + +static s32 GetTypeIconBounceMovement(s32 originalY, u32 position) +{ + struct Sprite* healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]]; + return originalY + healthbox->y2; +} +