pokeemmo/src/battle_debug.c

2284 lines
81 KiB
C

#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_message.h"
#include "main.h"
#include "menu.h"
#include "menu_helpers.h"
#include "scanline_effect.h"
#include "palette.h"
#include "party_menu.h"
#include "pokemon_icon.h"
#include "sprite.h"
#include "item.h"
#include "task.h"
#include "bg.h"
#include "gpu_regs.h"
#include "window.h"
#include "text.h"
#include "text_window.h"
#include "international_string_util.h"
#include "strings.h"
#include "battle_ai_main.h"
#include "battle_ai_util.h"
#include "list_menu.h"
#include "decompress.h"
#include "trainer_pokemon_sprites.h"
#include "malloc.h"
#include "string_util.h"
#include "util.h"
#include "data.h"
#include "reset_rtc_screen.h"
#include "reshow_battle_screen.h"
#include "constants/abilities.h"
#include "constants/party_menu.h"
#include "constants/moves.h"
#include "constants/items.h"
#include "constants/rgb.h"
#include "constants/hold_effects.h"
#define MAX_MODIFY_DIGITS 4
struct BattleDebugModifyArrows
{
u8 arrowSpriteId[2];
u16 minValue;
u16 maxValue;
int currValue;
u8 currentDigit:4;
u8 maxDigits:4;
u8 charDigits[MAX_MODIFY_DIGITS];
void *modifiedValPtr;
u8 typeOfVal;
};
struct BattleDebugMenu
{
u8 battlerId:2;
u8 aiBattlerId:2;
u8 battlerWindowId;
u8 mainListWindowId;
u8 mainListTaskId;
u8 currentMainListItemId;
u8 secondaryListWindowId;
u8 secondaryListTaskId;
u8 currentSecondaryListItemId;
u8 secondaryListItemCount;
u8 modifyWindowId;
u8 activeWindow;
struct BattleDebugModifyArrows modifyArrows;
const struct BitfieldInfo *bitfield;
bool8 battlerWasChanged[MAX_BATTLERS_COUNT];
u8 aiViewState;
u8 aiMonSpriteId;
u8 aiMovesWindowId;
union
{
u8 aiIconSpriteIds[MAX_BATTLERS_COUNT];
u8 aiPartyIcons[PARTY_SIZE];
} spriteIds;
};
struct __attribute__((__packed__)) BitfieldInfo
{
u8 bitsCount;
u8 currBit;
};
enum
{
LIST_ITEM_MOVES,
LIST_ITEM_ABILITY,
LIST_ITEM_HELD_ITEM,
LIST_ITEM_PP,
LIST_ITEM_TYPES,
LIST_ITEM_STATS,
LIST_ITEM_STAT_STAGES,
LIST_ITEM_STATUS1,
LIST_ITEM_VOLATILE,
LIST_ITEM_HAZARDS,
LIST_ITEM_SIDE_STATUS,
LIST_ITEM_AI,
LIST_ITEM_AI_MOVES_PTS,
LIST_ITEM_AI_INFO,
LIST_ITEM_AI_PARTY,
LIST_ITEM_VARIOUS,
LIST_ITEM_INSTANT_WIN,
LIST_ITEM_COUNT
};
enum
{
LIST_STAT_HP_CURRENT,
LIST_STAT_HP_MAX,
LIST_STAT_ATTACK,
LIST_STAT_DEFENSE,
LIST_STAT_SPEED,
LIST_STAT_SP_ATK,
LIST_STAT_SP_DEF,
};
enum
{
LIST_STATUS1_SLEEP,
LIST_STATUS1_POISON,
LIST_STATUS1_BURN,
LIST_STATUS1_FREEZE,
LIST_STATUS1_PARALYSIS,
LIST_STATUS1_TOXIC_POISON,
LIST_STATUS1_TOXIC_COUNTER,
LIST_STATUS1_FROSTBITE,
};
enum
{
LIST_SIDE_STICKY_WEB,
LIST_SIDE_SPIKES,
LIST_SIDE_TOXIC_SPIKES,
LIST_SIDE_STEALTH_ROCK,
LIST_SIDE_STEELSURGE,
};
enum
{
LIST_SIDE_REFLECT,
LIST_SIDE_LIGHTSCREEN,
LIST_SIDE_SAFEGUARD,
LIST_SIDE_MIST,
LIST_SIDE_TAILWIND,
LIST_SIDE_AURORA_VEIL,
LIST_SIDE_LUCKY_CHANT,
LIST_SIDE_DAMAGE_NON_TYPES,
LIST_SIDE_RAINBOW,
LIST_SIDE_SEA_OF_FIRE,
LIST_SIDE_SWAMP,
};
enum
{
LIST_AI_CHECK_BAD_MOVE,
LIST_AI_TRY_TO_FAINT,
LIST_AI_CHECK_VIABILITY,
LIST_AI_FORCE_SETUP_FIRST_TURN,
LIST_AI_RISKY,
LIST_AI_TRY_TO_2HKO,
LIST_AI_PREFER_BATON_PASS,
LIST_AI_DOUBLE_BATTLE,
LIST_AI_HP_AWARE,
LIST_AI_POWERFUL_STATUS,
LIST_AI_NEGATE_UNAWARE,
LIST_AI_WILL_SUICIDE,
LIST_AI_PREFER_STATUS_MOVES,
LIST_AI_STALL,
LIST_AI_SMART_SWITCHING,
LIST_AI_ACE_POKEMON,
LIST_AI_OMNISCIENT,
LIST_AI_SMART_MON_CHOICES,
LIST_AI_CONSERVATIVE,
LIST_AI_SEQUENCE_SWITCHING,
LIST_AI_DOUBLE_ACE_POKEMON,
LIST_AI_WEIGH_ABILITY_PREDICTION,
LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE,
LIST_AI_PREDICT_SWITCH,
LIST_AI_PREDICT_INCOMING_MON,
LIST_AI_DYNAMIC_FUNC,
LIST_AI_ROAMING,
LIST_AI_SAFARI,
LIST_AI_FIRST_BATTLE,
};
enum
{
VARIOUS_SHOW_HP,
VARIOUS_SUBSTITUTE_HP,
VARIOUS_IN_LOVE,
};
enum
{
ACTIVE_WIN_MAIN,
ACTIVE_WIN_SECONDARY,
ACTIVE_WIN_MODIFY
};
enum
{
VAL_U8,
VAL_U16,
VAL_U32,
VAL_BITFIELD_8,
VAL_BITFIELD_16,
VAL_BITFIELD_32,
VAL_VOLATILE,
VAL_HAZARDS,
VAR_SIDE_STATUS,
VAR_SHOW_HP,
VAR_SUBSTITUTE,
VAR_IN_LOVE,
VAR_U16_4_ENTRIES,
VAL_S8,
VAL_ALL_STAT_STAGES,
};
// Static Declarations
static const u8 *GetHoldEffectName(enum ItemHoldEffect holdEffect);
// const rom data
static const u8 sText_Ability[] = _("Ability");
static const u8 sText_HeldItem[] = _("Held Item");
static const u8 sText_HoldEffect[] = _("Hold Effect");
static const u8 sText_EmptyString[] = _("");
static const struct BitfieldInfo sStatus1Bitfield[] =
{
{/*Sleep*/ 3, 0},
{/*Poison*/ 1, 3},
{/*Burn*/ 1, 4},
{/*Freeze*/ 1, 5},
{/*Paralysis*/1, 6},
{/*Toxic Poison*/ 1, 7},
{/*Toxic Counter*/ 4, 8},
{/*Frostbite*/ 1, 12},
};
static const struct BitfieldInfo sStatus3Bitfield[] =
{
{/*Leech Seed Battler*/ 2, 0},
{/*Leech Seed*/ 1, 2},
{/*Always Hits*/ 2, 3},
{/*Perish Song*/ 1, 5},
{/*On Air*/ 1, 6},
{/*Underground*/ 1, 7},
{/*Minimized*/ 1, 8},
{/*Charged Up*/ 1, 9},
{/*Rooted*/ 1, 10},
{/*Yawn*/ 2, 11},
{/*Imprisoned Others*/ 1, 13},
{/*Grudge*/ 1, 14},
{/*Gastro Acid*/ 1, 16},
{/*Embargo*/ 1, 17},
{/*Underwater*/ 1, 18},
{/*Smacked Down*/ 1, 21},
{/*Telekinesis*/ 1, 23},
{/*Miracle Eyed*/ 1, 25},
{/*Magnet Rise*/ 1, 26},
{/*Heal Blocked*/ 1, 27},
{/*Aqua Ring*/ 1, 28},
{/*Laser Focus*/ 1, 29},
{/*Power Trick*/ 1, 30},
};
static const struct BitfieldInfo sAIBitfield[] =
{
{/*Check Bad Move*/ 1, 0},
{/*Try to Faint*/ 1, 1},
{/*Check Viability*/ 1, 2},
{/*Force Setup First Turn*/ 1, 3},
{/*Risky*/ 1, 4},
{/*Prefer Strongest Move*/ 1, 5},
{/*Prefer Baton Pass*/ 1, 6},
{/*Double Battle*/ 1, 7},
{/*HP Aware*/ 1, 8},
{/*Powerful Status*/ 1, 9},
{/*Negate Unaware*/ 1, 10},
{/*Will Suicide*/ 1, 11},
{/*Prefer Status Moves*/ 1, 12},
{/*Stall*/ 1, 13},
{/*Smart Switching*/ 1, 14},
{/*Ace Pokemon*/ 1, 15},
{/*Omniscient*/ 1, 16},
{/*Smart Mon Choices*/ 1, 17},
{/*Conservative*/ 1, 18},
{/*Sequence Switching*/ 1, 19},
{/*Double Ace Pokemon*/ 1, 20},
{/*Weigh Ability Prediction*/ 1, 21},
{/*Prefer Highest Damage Move*/ 1, 22},
{/*Predict Switch*/ 1, 23},
{/*Predict Incoming Mon*/ 1, 24},
{/*Dynamic Func*/ 1, 28},
{/*Roaming*/ 1, 29},
{/*Safari*/ 1, 30},
{/*First Battle*/ 1, 31},
};
static const struct ListMenuItem sMainListItems[] =
{
{COMPOUND_STRING("Moves"), LIST_ITEM_MOVES},
{sText_Ability, LIST_ITEM_ABILITY},
{sText_HeldItem, LIST_ITEM_HELD_ITEM},
{COMPOUND_STRING("PP"), LIST_ITEM_PP},
{COMPOUND_STRING("Types"), LIST_ITEM_TYPES},
{COMPOUND_STRING("Stats"), LIST_ITEM_STATS},
{COMPOUND_STRING("Stat Stages"), LIST_ITEM_STAT_STAGES},
{COMPOUND_STRING("Status1"), LIST_ITEM_STATUS1},
{COMPOUND_STRING("Volatiles"), LIST_ITEM_VOLATILE},
{COMPOUND_STRING("Hazards"), LIST_ITEM_HAZARDS},
{COMPOUND_STRING("Side Status"), LIST_ITEM_SIDE_STATUS},
{COMPOUND_STRING("AI"), LIST_ITEM_AI},
{COMPOUND_STRING("AI Pts/Dmg"), LIST_ITEM_AI_MOVES_PTS},
{COMPOUND_STRING("AI Info"), LIST_ITEM_AI_INFO},
{COMPOUND_STRING("AI Party"), LIST_ITEM_AI_PARTY},
{COMPOUND_STRING("Various"), LIST_ITEM_VARIOUS},
{COMPOUND_STRING("Instant Win"), LIST_ITEM_INSTANT_WIN},
};
static const struct ListMenuItem sStatsListItems[] =
{
{COMPOUND_STRING("HP Current"), LIST_STAT_HP_CURRENT},
{COMPOUND_STRING("HP Max"), LIST_STAT_HP_MAX},
{COMPOUND_STRING("Attack"), LIST_STAT_ATTACK},
{COMPOUND_STRING("Defense"), LIST_STAT_DEFENSE},
{COMPOUND_STRING("Speed"), LIST_STAT_SPEED},
{COMPOUND_STRING("Sp. Atk"), LIST_STAT_SP_ATK},
{COMPOUND_STRING("Sp. Def"), LIST_STAT_SP_DEF},
};
static const struct ListMenuItem sStatus1ListItems[] =
{
{COMPOUND_STRING("Sleep"), LIST_STATUS1_SLEEP},
{COMPOUND_STRING("Poison"), LIST_STATUS1_POISON},
{COMPOUND_STRING("Burn"), LIST_STATUS1_BURN},
{COMPOUND_STRING("Freeze"), LIST_STATUS1_FREEZE},
{COMPOUND_STRING("Paralysis"), LIST_STATUS1_PARALYSIS},
{COMPOUND_STRING("Toxic Poison"), LIST_STATUS1_TOXIC_POISON},
{COMPOUND_STRING("Toxic Counter"), LIST_STATUS1_TOXIC_COUNTER},
{COMPOUND_STRING("Frostbite"), LIST_STATUS1_FROSTBITE},
};
static const struct ListMenuItem sVolatileStatusListItems[] =
{
{COMPOUND_STRING("Confusion"), VOLATILE_CONFUSION},
{COMPOUND_STRING("Flinched"), VOLATILE_FLINCHED},
{COMPOUND_STRING("Torment"), VOLATILE_TORMENT},
{COMPOUND_STRING("Powder"), VOLATILE_POWDER},
{COMPOUND_STRING("DefenseCurl"), VOLATILE_DEFENSE_CURL},
{COMPOUND_STRING("Recharge"), VOLATILE_RECHARGE},
{COMPOUND_STRING("Rage"), VOLATILE_RAGE},
{COMPOUND_STRING("DestinyBond"), VOLATILE_DESTINY_BOND},
{COMPOUND_STRING("EscapePrevention"), VOLATILE_ESCAPE_PREVENTION},
{COMPOUND_STRING("Cursed"), VOLATILE_CURSED},
{COMPOUND_STRING("Foresight"), VOLATILE_FORESIGHT},
{COMPOUND_STRING("DragonCheer"), VOLATILE_DRAGON_CHEER},
{COMPOUND_STRING("FocusEnergy"), VOLATILE_FOCUS_ENERGY},
{COMPOUND_STRING("Electrified"), VOLATILE_ELECTRIFIED},
{COMPOUND_STRING("MudSport"), VOLATILE_MUD_SPORT},
{COMPOUND_STRING("WaterSport"), VOLATILE_WATER_SPORT},
{COMPOUND_STRING("Infinite Confusion"), VOLATILE_INFINITE_CONFUSION},
{COMPOUND_STRING("Salt Cure"), VOLATILE_SALT_CURE},
{COMPOUND_STRING("Syrup Bomb"), VOLATILE_SYRUP_BOMB},
{COMPOUND_STRING("Glaive Rush"), VOLATILE_GLAIVE_RUSH},
{COMPOUND_STRING("Leech Seed"), VOLATILE_LEECH_SEED},
{COMPOUND_STRING("Lock On"), VOLATILE_LOCK_ON},
{COMPOUND_STRING("Perish Song"), VOLATILE_PERISH_SONG},
{COMPOUND_STRING("Minimize"), VOLATILE_MINIMIZE},
{COMPOUND_STRING("Charge"), VOLATILE_CHARGE},
{COMPOUND_STRING("Root"), VOLATILE_ROOT},
{COMPOUND_STRING("Yawn"), VOLATILE_YAWN},
{COMPOUND_STRING("Imprison"), VOLATILE_IMPRISON},
{COMPOUND_STRING("Grudge"), VOLATILE_GRUDGE},
{COMPOUND_STRING("Gastro Acid"), VOLATILE_GASTRO_ACID},
{COMPOUND_STRING("Embargo"), VOLATILE_EMBARGO},
{COMPOUND_STRING("Smack Down"), VOLATILE_SMACK_DOWN},
{COMPOUND_STRING("Telekinesis"), VOLATILE_TELEKINESIS},
{COMPOUND_STRING("Miracle Eye"), VOLATILE_MIRACLE_EYE},
{COMPOUND_STRING("Magnet Rise"), VOLATILE_MAGNET_RISE},
{COMPOUND_STRING("Heal Block"), VOLATILE_HEAL_BLOCK},
{COMPOUND_STRING("Aqua Ring"), VOLATILE_AQUA_RING},
{COMPOUND_STRING("Laser Focus"), VOLATILE_LASER_FOCUS},
{COMPOUND_STRING("Power Trick"), VOLATILE_POWER_TRICK},
};
static const struct ListMenuItem sHazardsListItems[] =
{
{COMPOUND_STRING("Spikes"), LIST_SIDE_SPIKES},
{COMPOUND_STRING("Sticky Web"), LIST_SIDE_STICKY_WEB},
{COMPOUND_STRING("Toxic Spikes"), LIST_SIDE_TOXIC_SPIKES},
{COMPOUND_STRING("Stealth Rock"), LIST_SIDE_STEALTH_ROCK},
{COMPOUND_STRING("Steelsurge"), LIST_SIDE_STEELSURGE},
};
static const struct ListMenuItem sSideStatusListItems[] =
{
{COMPOUND_STRING("Reflect"), LIST_SIDE_REFLECT},
{COMPOUND_STRING("Light Screen"), LIST_SIDE_LIGHTSCREEN},
{COMPOUND_STRING("Safeguard"), LIST_SIDE_SAFEGUARD},
{COMPOUND_STRING("Mist"), LIST_SIDE_MIST},
{COMPOUND_STRING("Tailwind"), LIST_SIDE_TAILWIND},
{COMPOUND_STRING("Aurora Veil"), LIST_SIDE_AURORA_VEIL},
{COMPOUND_STRING("Lucky Chant"), LIST_SIDE_LUCKY_CHANT},
{COMPOUND_STRING("Damage Non-Types"), LIST_SIDE_DAMAGE_NON_TYPES},
{COMPOUND_STRING("Rainbow"), LIST_SIDE_RAINBOW},
{COMPOUND_STRING("Sea of Fire"), LIST_SIDE_SEA_OF_FIRE},
{COMPOUND_STRING("Swamp"), LIST_SIDE_SWAMP},
};
static const struct ListMenuItem sAIListItems[] =
{
{COMPOUND_STRING("Check Bad Move"), LIST_AI_CHECK_BAD_MOVE},
{COMPOUND_STRING("Try to Faint"), LIST_AI_TRY_TO_FAINT},
{COMPOUND_STRING("Check Viability"), LIST_AI_CHECK_VIABILITY},
{COMPOUND_STRING("Force Setup First Turn"), LIST_AI_FORCE_SETUP_FIRST_TURN},
{COMPOUND_STRING("Risky"), LIST_AI_RISKY},
{COMPOUND_STRING("Try to 2HKO"), LIST_AI_TRY_TO_2HKO},
{COMPOUND_STRING("Prefer Baton Pass"), LIST_AI_PREFER_BATON_PASS},
{COMPOUND_STRING("Double Battle"), LIST_AI_DOUBLE_BATTLE},
{COMPOUND_STRING("HP Aware"), LIST_AI_HP_AWARE},
{COMPOUND_STRING("Powerful Status"), LIST_AI_POWERFUL_STATUS},
{COMPOUND_STRING("Negate Unaware"), LIST_AI_NEGATE_UNAWARE},
{COMPOUND_STRING("Will Suicide"), LIST_AI_WILL_SUICIDE},
{COMPOUND_STRING("Prefer Status Moves"), LIST_AI_PREFER_STATUS_MOVES},
{COMPOUND_STRING("Stall"), LIST_AI_STALL},
{COMPOUND_STRING("Smart Switching"), LIST_AI_SMART_SWITCHING},
{COMPOUND_STRING("Ace Pokémon"), LIST_AI_ACE_POKEMON},
{COMPOUND_STRING("Omniscient"), LIST_AI_OMNISCIENT},
{COMPOUND_STRING("Smart Mon Choices"), LIST_AI_SMART_MON_CHOICES},
{COMPOUND_STRING("Conservative"), LIST_AI_CONSERVATIVE},
{COMPOUND_STRING("Sequence Switching"), LIST_AI_SEQUENCE_SWITCHING},
{COMPOUND_STRING("Double Ace Pokémon"), LIST_AI_DOUBLE_ACE_POKEMON},
{COMPOUND_STRING("Weigh Ability Prediction"), LIST_AI_WEIGH_ABILITY_PREDICTION},
{COMPOUND_STRING("Prefer Highest Damage Move"), LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE},
{COMPOUND_STRING("Predict Switch"), LIST_AI_PREDICT_SWITCH},
{COMPOUND_STRING("Predict Incoming Mon"), LIST_AI_PREDICT_INCOMING_MON},
{COMPOUND_STRING("Dynamic Func"), LIST_AI_DYNAMIC_FUNC},
{COMPOUND_STRING("Roaming"), LIST_AI_ROAMING},
{COMPOUND_STRING("Safari"), LIST_AI_SAFARI},
{COMPOUND_STRING("First Battle"), LIST_AI_FIRST_BATTLE},
};
static const struct ListMenuItem sVariousListItems[] =
{
{COMPOUND_STRING("Show HP"), VARIOUS_SHOW_HP},
{COMPOUND_STRING("Substitute HP"), VARIOUS_SUBSTITUTE_HP},
{COMPOUND_STRING("In Love"), VARIOUS_IN_LOVE},
};
static const struct ListMenuItem sSecondaryListItems[] =
{
{sText_EmptyString, 0},
{sText_EmptyString, 1},
{sText_EmptyString, 2},
{sText_EmptyString, 3},
{sText_EmptyString, 4},
{sText_EmptyString, 5},
{sText_EmptyString, 6},
{sText_EmptyString, 7},
{sText_EmptyString, 8},
};
static const struct ListMenuTemplate sMainListTemplate =
{
.items = sMainListItems,
.moveCursorFunc = NULL,
.itemPrintFunc = NULL,
.totalItems = ARRAY_COUNT(sMainListItems),
.maxShowed = 6,
.windowId = 0,
.header_X = 0,
.item_X = 8,
.cursor_X = 0,
.upText_Y = 1,
.cursorPal = 2,
.fillValue = 1,
.cursorShadowPal = 3,
.lettersSpacing = 1,
.itemVerticalPadding = 0,
.scrollMultiple = LIST_NO_MULTIPLE_SCROLL,
.fontId = 1,
.cursorKind = 0
};
static const struct ListMenuTemplate sSecondaryListTemplate =
{
.items = sSecondaryListItems,
.moveCursorFunc = NULL,
.itemPrintFunc = NULL,
.totalItems = 0,
.maxShowed = 0,
.windowId = 0,
.header_X = 0,
.item_X = 8,
.cursor_X = 0,
.upText_Y = 1,
.cursorPal = 2,
.fillValue = 1,
.cursorShadowPal = 3,
.lettersSpacing = 1,
.itemVerticalPadding = 0,
.scrollMultiple = LIST_NO_MULTIPLE_SCROLL,
.fontId = 1,
.cursorKind = 0
};
static const struct WindowTemplate sMainListWindowTemplate =
{
.bg = 0,
.tilemapLeft = 1,
.tilemapTop = 3,
.width = 9,
.height = 12,
.paletteNum = 0xF,
.baseBlock = 0x1
};
static const struct WindowTemplate sSecondaryListWindowTemplate =
{
.bg = 0,
.tilemapLeft = 12,
.tilemapTop = 3,
.width = 20,
.height = 16,
.paletteNum = 0xF,
.baseBlock = 0x6D
};
static const struct WindowTemplate sModifyWindowTemplate =
{
.bg = 0,
.tilemapLeft = 25,
.tilemapTop = 2,
.width = 4,
.height = 2,
.paletteNum = 0xF,
.baseBlock = 0x1AD
};
static const struct WindowTemplate sBattlerWindowTemplate =
{
.bg = 0,
.tilemapLeft = 10,
.tilemapTop = 0,
.width = 14,
.height = 2,
.paletteNum = 0xF,
.baseBlock = 0x1B5
};
static const struct BgTemplate sBgTemplates[] =
{
{
.bg = 0,
.charBaseIndex = 0,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 2,
.mapBaseIndex = 20,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
}
};
static const bool8 sHasChangeableEntries[LIST_ITEM_COUNT] =
{
[LIST_ITEM_MOVES] = TRUE,
[LIST_ITEM_AI_MOVES_PTS] = TRUE,
[LIST_ITEM_PP] = TRUE,
[LIST_ITEM_ABILITY] = TRUE,
[LIST_ITEM_TYPES] = TRUE,
[LIST_ITEM_HELD_ITEM] = TRUE,
[LIST_ITEM_STAT_STAGES] = TRUE,
};
static const u16 sBgColor[] = {RGB_WHITE};
// this file's functions
static void Task_DebugMenuFadeOut(u8 taskId);
static void Task_DebugMenuProcessInput(u8 taskId);
static void Task_DebugMenuFadeIn(u8 taskId);
static void PrintOnBattlerWindow(u8 windowId, u8 battlerId);
static void UpdateWindowsOnChangedBattler(struct BattleDebugMenu *data);
static void CreateSecondaryListMenu(struct BattleDebugMenu *data);
static void PrintSecondaryEntries(struct BattleDebugMenu *data);
static void DestroyModifyArrows(struct BattleDebugMenu *data);
static void PrintDigitChars(struct BattleDebugMenu *data);
static void SetUpModifyArrows(struct BattleDebugMenu *data);
static void UpdateBattlerValue(struct BattleDebugMenu *data);
static void UpdateMonData(struct BattleDebugMenu *data);
static void ChangeHazardsValue(struct BattleDebugMenu *data);
static u32 GetHazardsValue(struct BattleDebugMenu *data);
static u16 *GetSideStatusValue(struct BattleDebugMenu *data, bool32 changeStatus, bool32 statusTrue);
static bool32 TryMoveDigit(struct BattleDebugModifyArrows *modArrows, bool32 moveUp);
static void SwitchToDebugView(u8 taskId);
static void SwitchToDebugViewFromAiParty(u8 taskId);
// code
static struct BattleDebugMenu *GetStructPtr(u8 taskId)
{
u8 *taskDataPtr = (u8 *)(&gTasks[taskId].data[0]);
return (struct BattleDebugMenu*)(T1_READ_PTR(taskDataPtr));
}
static void SetStructPtr(u8 taskId, void *ptr)
{
u32 structPtr = (u32)(ptr);
u8 *taskDataPtr = (u8 *)(&gTasks[taskId].data[0]);
taskDataPtr[0] = structPtr >> 0;
taskDataPtr[1] = structPtr >> 8;
taskDataPtr[2] = structPtr >> 16;
taskDataPtr[3] = structPtr >> 24;
}
static void MainCB2(void)
{
RunTasks();
AnimateSprites();
BuildOamBuffer();
UpdatePaletteFade();
}
static void VBlankCB(void)
{
LoadOam();
ProcessSpriteCopyRequests();
TransferPlttBuffer();
}
void CB2_BattleDebugMenu(void)
{
u8 taskId;
struct BattleDebugMenu *data;
switch (gMain.state)
{
default:
case 0:
SetVBlankCallback(NULL);
gMain.state++;
break;
case 1:
ResetVramOamAndBgCntRegs();
SetGpuReg(REG_OFFSET_DISPCNT, 0);
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
ResetAllBgsCoordinates();
FreeAllWindowBuffers();
DeactivateAllTextPrinters();
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
ShowBg(0);
ShowBg(1);
gMain.state++;
break;
case 2:
ResetPaletteFade();
ScanlineEffect_Stop();
ResetTasks();
ResetSpriteData();
gMain.state++;
break;
case 3:
LoadPalette(sBgColor, 0, 2);
LoadPalette(GetOverworldTextboxPalettePtr(), 0xf0, 16);
gMain.state++;
break;
case 4:
taskId = CreateTask(Task_DebugMenuFadeIn, 0);
data = AllocZeroed(sizeof(struct BattleDebugMenu));
SetStructPtr(taskId, data);
data->battlerId = gBattleStruct->debugBattler;
data->battlerWindowId = AddWindow(&sBattlerWindowTemplate);
PutWindowTilemap(data->battlerWindowId);
PrintOnBattlerWindow(data->battlerWindowId, data->battlerId);
data->mainListWindowId = AddWindow(&sMainListWindowTemplate);
gMultiuseListMenuTemplate = sMainListTemplate;
gMultiuseListMenuTemplate.windowId = data->mainListWindowId;
data->mainListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, 0, 0);
data->currentMainListItemId = 0;
data->activeWindow = ACTIVE_WIN_MAIN;
data->secondaryListTaskId = 0xFF;
CopyWindowToVram(data->mainListWindowId, COPYWIN_FULL);
gMain.state++;
break;
case 5:
BeginNormalPaletteFade(-1, 0, 0x10, 0, 0);
SetVBlankCallback(VBlankCB);
SetMainCallback2(MainCB2);
return;
}
}
static void PutMovesPointsText(struct BattleDebugMenu *data)
{
u32 i, j, count, battlerDef;
u8 *text = Alloc(0x50);
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
for (i = 0; i < MAX_MON_MOVES; i++)
{
text[0] = CHAR_SPACE;
StringCopy(text + 1, GetMoveName(gBattleMons[data->aiBattlerId].moves[i]));
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 0, i * 15, 0, NULL);
for (count = 0, j = 0; j < MAX_BATTLERS_COUNT; j++)
{
if (data->spriteIds.aiIconSpriteIds[j] == 0xFF)
continue;
battlerDef = gSprites[data->spriteIds.aiIconSpriteIds[j]].data[0];
ConvertIntToDecimalStringN(text,
gAiBattleData->finalScore[data->aiBattlerId][battlerDef][i],
STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 83 + count * 54, i * 15, 0, NULL);
ConvertIntToDecimalStringN(text,
AI_GetDamage(data->aiBattlerId, battlerDef, i, AI_ATTACKING, gAiLogicData),
STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 110 + count * 54, i * 15, 0, NULL);
count++;
}
}
if (gAiLogicData->shouldSwitch & (1u << data->aiBattlerId))
{
u32 switchMon = GetMonData(&gEnemyParty[gAiLogicData->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, COMPOUND_STRING("Switching to "), 74, 64, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, gSpeciesInfo[switchMon].speciesName, 74 + 68, 64, 0, NULL);
}
else
{
u32 chosenMoveIndex = gAiBattleData->chosenMoveIndex[data->aiBattlerId];
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, COMPOUND_STRING("Chosen move: "), 74, 64, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, GetMoveName(gBattleMons[data->aiBattlerId].moves[chosenMoveIndex]), 74 + 68, 64, 0, NULL);
}
CopyWindowToVram(data->aiMovesWindowId, COPYWIN_FULL);
Free(text);
}
static void CleanUpAiInfoWindow(u8 taskId)
{
u32 i;
struct BattleDebugMenu *data = GetStructPtr(taskId);
FreeMonIconPalettes();
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (data->spriteIds.aiIconSpriteIds[i] != 0xFF)
FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiIconSpriteIds[i]]);
}
FreeAndDestroyMonPicSprite(data->aiMonSpriteId);
ClearWindowTilemap(data->aiMovesWindowId);
RemoveWindow(data->aiMovesWindowId);
}
static void Task_ShowAiPoints(u8 taskId)
{
u32 i, count;
struct WindowTemplate winTemplate;
struct BattleDebugMenu *data = GetStructPtr(taskId);
struct Pokemon *mon;
switch (data->aiViewState)
{
case 0:
HideBg(0);
ShowBg(1);
// Swap battler if it's player mon
data->aiBattlerId = data->battlerId;
while (!BattlerHasAi(data->aiBattlerId))
{
if (++data->aiBattlerId >= gBattlersCount)
data->aiBattlerId = 0;
}
data->battlerId = data->aiBattlerId;
LoadMonIconPalettes();
for (count = 0, i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (i != data->aiBattlerId && IsBattlerAlive(i))
{
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
SpriteCallbackDummy,
95 + (count * 60), 17, 0, 0);
gSprites[data->spriteIds.aiIconSpriteIds[i]].data[0] = i; // battler id
count++;
}
else
{
data->spriteIds.aiIconSpriteIds[i] = 0xFF;
}
}
mon = GetBattlerMon(data->aiBattlerId);
data->aiMonSpriteId = CreateMonPicSprite(gBattleMons[data->aiBattlerId].species,
GetMonData(mon, MON_DATA_IS_SHINY),
gBattleMons[data->aiBattlerId].personality,
TRUE,
39, 130, 15, TAG_NONE);
data->aiViewState++;
break;
// Put text
case 1:
winTemplate = CreateWindowTemplate(1, 0, 4, 30, 14, 15, 0x200);
data->aiMovesWindowId = AddWindow(&winTemplate);
PutWindowTilemap(data->aiMovesWindowId);
PutMovesPointsText(data);
data->aiViewState++;
break;
// Input
case 2:
if (JOY_NEW(R_BUTTON) && IsDoubleBattle())
{
CleanUpAiInfoWindow(taskId);
do {
data->battlerId++;
data->battlerId %= gBattlersCount;
} while (!IsBattlerAlive(data->battlerId));
data->aiViewState = 0;
}
else if (JOY_NEW(L_BUTTON) && IsDoubleBattle())
{
CleanUpAiInfoWindow(taskId);
do {
if (data->battlerId == 0)
data->battlerId = gBattlersCount - 1;
else
data->battlerId--;
} while (!IsBattlerAlive(data->battlerId) || !BattlerHasAi(data->battlerId));
data->aiViewState = 0;
}
else if (JOY_NEW(SELECT_BUTTON | B_BUTTON))
{
SwitchToDebugView(taskId);
HideBg(1);
ShowBg(0);
return;
}
break;
}
}
static void SwitchToAiPointsView(u8 taskId)
{
gTasks[taskId].func = Task_ShowAiPoints;
GetStructPtr(taskId)->aiViewState = 0;
}
static const u8 *const sAiInfoItemNames[] =
{
sText_Ability,
sText_HeldItem,
sText_HoldEffect,
};
static void PutAiInfoText(struct BattleDebugMenu *data)
{
u32 i;
u8 *text = Alloc(0x50);
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
// item names
for (i = 0; i < ARRAY_COUNT(sAiInfoItemNames); i++)
{
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, sAiInfoItemNames[i], 3, i * 15, 0, NULL);
}
// items info
for (i = 0; i < gBattlersCount; i++)
{
if (IsOnPlayerSide(i) && IsBattlerAlive(i))
{
u16 ability = gAiLogicData->abilities[i];
enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[i];
u16 item = gAiLogicData->items[i];
u8 x = (i == B_POSITION_PLAYER_LEFT) ? 83 + (i) * 75 : 83 + (i-1) * 75;
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, gAbilitiesInfo[ability].name, x, 0, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, GetItemName(item), x, 15, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, GetHoldEffectName(holdEffect), x, 30, 0, NULL);
}
}
CopyWindowToVram(data->aiMovesWindowId, COPYWIN_FULL);
Free(text);
}
static void PutAiPartyText(struct BattleDebugMenu *data)
{
u32 i, j, count;
u8 *text = Alloc(0x50), *txtPtr;
struct AiPartyMon *aiMons = gAiPartyData->mons[GetBattlerSide(data->aiBattlerId)];
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
count = gAiPartyData->count[GetBattlerSide(data->aiBattlerId)];
for (i = 0; i < count; i++)
{
if (aiMons[i].wasSentInBattle)
{
text[0] = CHAR_LV;
txtPtr = ConvertIntToDecimalStringN(text + 1, aiMons[i].level, STR_CONV_MODE_LEFT_ALIGN, 3);
*txtPtr++ = CHAR_SPACE;
if (aiMons[i].gender == MON_MALE)
*txtPtr++ = CHAR_MALE;
else if (aiMons[i].gender == MON_FEMALE)
*txtPtr++ = CHAR_FEMALE;
*txtPtr = EOS;
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 0, 0, NULL, 0, 0);
}
txtPtr = StringCopyN(text, gAbilitiesInfo[aiMons[i].ability].name, 7); // The screen is too small to fit the whole string, so we need to drop the last letters.
*txtPtr = EOS;
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 15, 0, NULL, 0, 0);
for (j = 0; j < MAX_MON_MOVES; j++)
{
txtPtr = StringCopyN(text, GetMoveName(aiMons[i].moves[j]), 8);
*txtPtr = EOS;
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 35 + j * 15, 0, NULL, 0, 0);
}
txtPtr = StringCopyN(text, GetHoldEffectName(aiMons[i].heldEffect), 7);
*txtPtr = EOS;
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 35 + j * 15, 0, NULL, 0, 0);
txtPtr = ConvertIntToDecimalStringN(text, aiMons[i].switchInCount, STR_CONV_MODE_LEFT_ALIGN, 2);
*txtPtr = EOS;
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 35 + (j + 1) * 15, 0, NULL, 0, 0);
}
CopyWindowToVram(data->aiMovesWindowId, COPYWIN_FULL);
Free(text);
}
static void Task_ShowAiKnowledge(u8 taskId)
{
u32 i, count;
struct WindowTemplate winTemplate;
struct BattleDebugMenu *data = GetStructPtr(taskId);
struct Pokemon *mon;
switch (data->aiViewState)
{
case 0:
HideBg(0);
ShowBg(1);
// Swap battler if it's player mon
data->aiBattlerId = data->battlerId;
while (!BattlerHasAi(data->aiBattlerId))
{
if (++data->aiBattlerId >= gBattlersCount)
data->aiBattlerId = 0;
}
LoadMonIconPalettes();
for (count = 0, i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (IsOnPlayerSide(i) && IsBattlerAlive(i))
{
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
SpriteCallbackDummy,
95 + (count * 80), 17, 0, 0);
gSprites[data->spriteIds.aiIconSpriteIds[i]].data[0] = i; // battler id
count++;
}
else
{
data->spriteIds.aiIconSpriteIds[i] = 0xFF;
}
}
mon = GetBattlerMon(data->aiBattlerId);
data->aiMonSpriteId = CreateMonPicSprite(gBattleMons[data->aiBattlerId].species,
GetMonData(mon, MON_DATA_IS_SHINY),
gBattleMons[data->aiBattlerId].personality,
TRUE,
39, 130, 15, TAG_NONE);
data->aiViewState++;
break;
// Put text
case 1:
winTemplate = CreateWindowTemplate(1, 0, 4, 27, 14, 15, 0x200);
data->aiMovesWindowId = AddWindow(&winTemplate);
PutWindowTilemap(data->aiMovesWindowId);
PutAiInfoText(data);
data->aiViewState++;
break;
// Input
case 2:
if (JOY_NEW(SELECT_BUTTON | B_BUTTON))
{
SwitchToDebugView(taskId);
HideBg(1);
ShowBg(0);
return;
}
break;
}
}
#define sConditionSpriteId data[1]
static void Task_ShowAiParty(u8 taskId)
{
u32 i, ailment;
struct WindowTemplate winTemplate;
struct AiPartyMon *aiMons;
struct BattleDebugMenu *data = GetStructPtr(taskId);
switch (data->aiViewState)
{
case 0:
HideBg(0);
ShowBg(1);
LoadMonIconPalettes();
LoadPartyMenuAilmentGfx();
data->aiBattlerId = data->battlerId;
aiMons = gAiPartyData->mons[GetBattlerSide(data->aiBattlerId)];
for (i = 0; i < gAiPartyData->count[GetBattlerSide(data->aiBattlerId)]; i++)
{
u16 species = SPECIES_NONE; // Question mark
if (aiMons[i].wasSentInBattle && aiMons[i].species)
species = aiMons[i].species;
data->spriteIds.aiPartyIcons[i] = CreateMonIcon(species, SpriteCallbackDummy, (i * 41) + 15, 7, 1, 0);
gSprites[data->spriteIds.aiPartyIcons[i]].oam.priority = 0;
gSprites[data->spriteIds.aiPartyIcons[i]].sConditionSpriteId = CreateSprite(&gSpriteTemplate_StatusIcons, (i * 41) + 15, 7, 0);
gSprites[gSprites[data->spriteIds.aiPartyIcons[i]].sConditionSpriteId].oam.priority = 0;
if (aiMons[i].isFainted)
ailment = AILMENT_FNT;
else
ailment = GetAilmentFromStatus(aiMons[i].status);
if (ailment != AILMENT_NONE)
StartSpriteAnim(&gSprites[gSprites[data->spriteIds.aiPartyIcons[i]].sConditionSpriteId], ailment - 1);
else
gSprites[gSprites[data->spriteIds.aiPartyIcons[i]].sConditionSpriteId].invisible = TRUE;
}
for (; i < PARTY_SIZE; i++)
data->spriteIds.aiPartyIcons[i] = 0xFF;
data->aiViewState++;
break;
// Put text
case 1:
winTemplate = CreateWindowTemplate(1, 0, 3, 29, 16, 15, 0x150);
data->aiMovesWindowId = AddWindow(&winTemplate);
PutWindowTilemap(data->aiMovesWindowId);
PutAiPartyText(data);
data->aiViewState++;
break;
// Input
case 2:
if (JOY_NEW(SELECT_BUTTON | B_BUTTON))
{
SwitchToDebugViewFromAiParty(taskId);
HideBg(1);
ShowBg(0);
return;
}
break;
}
}
static void SwitchToAiInfoView(u8 taskId)
{
gTasks[taskId].func = Task_ShowAiKnowledge;
GetStructPtr(taskId)->aiViewState = 0;
}
static void SwitchToAiPartyView(u8 taskId)
{
gTasks[taskId].func = Task_ShowAiParty;
GetStructPtr(taskId)->aiViewState = 0;
}
static void SwitchToDebugViewFromAiParty(u8 taskId)
{
u32 i;
struct BattleDebugMenu *data = GetStructPtr(taskId);
FreeMonIconPalettes();
for (i = 0; i < PARTY_SIZE; i++)
{
if (data->spriteIds.aiPartyIcons[i] != 0xFF)
{
DestroySpriteAndFreeResources(&gSprites[gSprites[data->spriteIds.aiPartyIcons[i]].sConditionSpriteId]);
FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiPartyIcons[i]]);
}
}
ClearWindowTilemap(data->aiMovesWindowId);
RemoveWindow(data->aiMovesWindowId);
gTasks[taskId].func = Task_DebugMenuProcessInput;
}
#undef sConditionSpriteId
static void SwitchToDebugView(u8 taskId)
{
CleanUpAiInfoWindow(taskId);
gTasks[taskId].func = Task_DebugMenuProcessInput;
}
static void Task_DebugMenuFadeIn(u8 taskId)
{
if (!gPaletteFade.active)
gTasks[taskId].func = Task_DebugMenuProcessInput;
}
static void Task_DebugMenuProcessInput(u8 taskId)
{
s32 listItemId = 0;
struct BattleDebugMenu *data = GetStructPtr(taskId);
// Exit the menu.
if (JOY_NEW(SELECT_BUTTON) || ((JOY_NEW(B_BUTTON)) && data->activeWindow == ACTIVE_WIN_MAIN))
{
BeginNormalPaletteFade(-1, 0, 0, 0x10, 0);
gTasks[taskId].func = Task_DebugMenuFadeOut;
return;
}
// Try changing active battler.
if (JOY_NEW(R_BUTTON))
{
if (data->battlerId++ == gBattlersCount - 1)
data->battlerId = 0;
UpdateWindowsOnChangedBattler(data);
}
else if (JOY_NEW(L_BUTTON))
{
if (data->battlerId-- == 0)
data->battlerId = gBattlersCount - 1;
UpdateWindowsOnChangedBattler(data);
}
// A main list item is active, handle input.
if (data->activeWindow == ACTIVE_WIN_MAIN)
{
listItemId = ListMenu_ProcessInput(data->mainListTaskId);
if (listItemId != LIST_CANCEL && listItemId != LIST_NOTHING_CHOSEN && listItemId < LIST_ITEM_COUNT)
{
if (listItemId == LIST_ITEM_AI_MOVES_PTS && JOY_NEW(A_BUTTON))
{
SwitchToAiPointsView(taskId);
return;
}
else if (listItemId == LIST_ITEM_AI_INFO && JOY_NEW(A_BUTTON))
{
SwitchToAiInfoView(taskId);
return;
}
else if (listItemId == LIST_ITEM_AI_PARTY && JOY_NEW(A_BUTTON))
{
SwitchToAiPartyView(taskId);
return;
}
else if (listItemId == LIST_ITEM_INSTANT_WIN && JOY_NEW(A_BUTTON))
{
BattleDebug_WonBattle();
BeginNormalPaletteFade(-1, 0, 0, 0x10, 0);
gTasks[taskId].func = Task_DebugMenuFadeOut;
return;
}
data->currentMainListItemId = listItemId;
// Create the secondary menu list.
CreateSecondaryListMenu(data);
PrintSecondaryEntries(data);
data->activeWindow = ACTIVE_WIN_SECONDARY;
}
}
// Secondary list is active, handle input.
else if (data->activeWindow == ACTIVE_WIN_SECONDARY)
{
listItemId = ListMenu_ProcessInput(data->secondaryListTaskId);
if (listItemId == LIST_CANCEL)
{
DestroyListMenuTask(data->secondaryListTaskId, NULL, NULL);
ClearStdWindowAndFrameToTransparent(data->secondaryListWindowId, TRUE);
RemoveWindow(data->secondaryListWindowId);
data->activeWindow = ACTIVE_WIN_MAIN;
data->secondaryListTaskId = 0xFF;
}
else if (listItemId != LIST_NOTHING_CHOSEN)
{
data->currentSecondaryListItemId = listItemId;
data->modifyWindowId = AddWindow(&sModifyWindowTemplate);
PutWindowTilemap(data->modifyWindowId);
CopyWindowToVram(data->modifyWindowId, COPYWIN_FULL);
SetUpModifyArrows(data);
PrintDigitChars(data);
data->activeWindow = ACTIVE_WIN_MODIFY;
}
}
// Handle value modifying.
else if (data->activeWindow == ACTIVE_WIN_MODIFY)
{
if (JOY_NEW(B_BUTTON | A_BUTTON))
{
ClearStdWindowAndFrameToTransparent(data->modifyWindowId, TRUE);
RemoveWindow(data->modifyWindowId);
DestroyModifyArrows(data);
data->activeWindow = ACTIVE_WIN_SECONDARY;
}
else if (JOY_NEW(DPAD_RIGHT))
{
if (data->modifyArrows.currentDigit != (data->modifyArrows.maxDigits - 1))
{
data->modifyArrows.currentDigit++;
gSprites[data->modifyArrows.arrowSpriteId[0]].x2 += 6;
gSprites[data->modifyArrows.arrowSpriteId[1]].x2 += 6;
}
}
else if (JOY_NEW(DPAD_LEFT))
{
if (data->modifyArrows.currentDigit != 0)
{
data->modifyArrows.currentDigit--;
gSprites[data->modifyArrows.arrowSpriteId[0]].x2 -= 6;
gSprites[data->modifyArrows.arrowSpriteId[1]].x2 -= 6;
}
}
else if (JOY_NEW(DPAD_UP))
{
if (TryMoveDigit(&data->modifyArrows, TRUE))
{
PrintDigitChars(data);
UpdateBattlerValue(data);
PrintSecondaryEntries(data);
}
}
else if (JOY_NEW(DPAD_DOWN))
{
if (TryMoveDigit(&data->modifyArrows, FALSE))
{
PrintDigitChars(data);
UpdateBattlerValue(data);
PrintSecondaryEntries(data);
}
}
}
}
static void Task_DebugMenuFadeOut(u8 taskId)
{
if (!gPaletteFade.active)
{
struct BattleDebugMenu *data = GetStructPtr(taskId);
DestroyListMenuTask(data->mainListTaskId, 0, 0);
if (data->secondaryListTaskId != 0xFF)
DestroyListMenuTask(data->secondaryListTaskId, 0, 0);
FreeAllWindowBuffers();
UpdateMonData(data);
gBattleStruct->debugBattler = data->battlerId;
Free(data);
DestroyTask(taskId);
SetMainCallback2(ReshowBattleScreenAfterMenu);
}
}
static void PrintOnBattlerWindow(u8 windowId, u8 battlerId)
{
u8 text[POKEMON_NAME_LENGTH + 10];
text[0] = CHAR_0 + battlerId;
text[1] = CHAR_SPACE;
text[2] = CHAR_HYPHEN;
text[3] = CHAR_SPACE;
StringCopy(&text[4], gBattleMons[battlerId].nickname);
FillWindowPixelBuffer(windowId, 0x11);
AddTextPrinterParameterized(windowId, FONT_NORMAL, text, 0, 0, 0, NULL);
CopyWindowToVram(windowId, COPYWIN_FULL);
}
static void UpdateWindowsOnChangedBattler(struct BattleDebugMenu *data)
{
PrintOnBattlerWindow(data->battlerWindowId, data->battlerId);
if (data->secondaryListTaskId != 0xFF)
{
DestroyListMenuTask(data->secondaryListTaskId, 0, 0);
RemoveWindow(data->secondaryListWindowId);
CreateSecondaryListMenu(data);
data->currentSecondaryListItemId = 0;
PrintSecondaryEntries(data);
}
if (data->activeWindow == ACTIVE_WIN_MODIFY)
{
DestroyModifyArrows(data);
SetUpModifyArrows(data);
PrintDigitChars(data);
}
}
static void CreateSecondaryListMenu(struct BattleDebugMenu *data)
{
struct WindowTemplate winTemplate;
struct ListMenuTemplate listTemplate;
u8 itemsCount = 1;
winTemplate = sSecondaryListWindowTemplate;
listTemplate = sSecondaryListTemplate;
switch (data->currentMainListItemId)
{
case LIST_ITEM_ABILITY:
itemsCount = 1;
break;
case LIST_ITEM_HELD_ITEM:
itemsCount = 1;
break;
case LIST_ITEM_TYPES:
itemsCount = 3;
break;
case LIST_ITEM_MOVES:
itemsCount = 5;
break;
case LIST_ITEM_PP:
itemsCount = 4;
break;
case LIST_ITEM_STATS:
listTemplate.items = sStatsListItems;
itemsCount = ARRAY_COUNT(sStatsListItems);
break;
case LIST_ITEM_STAT_STAGES:
itemsCount = 8;
break;
case LIST_ITEM_STATUS1:
listTemplate.items = sStatus1ListItems;
itemsCount = ARRAY_COUNT(sStatus1ListItems);
data->bitfield = sStatus1Bitfield;
break;
case LIST_ITEM_VOLATILE:
listTemplate.items = sVolatileStatusListItems;
itemsCount = ARRAY_COUNT(sVolatileStatusListItems);
break;
case LIST_ITEM_AI:
listTemplate.items = sAIListItems;
itemsCount = ARRAY_COUNT(sAIListItems);
data->bitfield = sAIBitfield;
break;
case LIST_ITEM_VARIOUS:
listTemplate.items = sVariousListItems;
itemsCount = ARRAY_COUNT(sVariousListItems);
break;
case LIST_ITEM_HAZARDS:
listTemplate.items = sHazardsListItems;
itemsCount = ARRAY_COUNT(sHazardsListItems);
break;
case LIST_ITEM_SIDE_STATUS:
listTemplate.items = sSideStatusListItems;
itemsCount = ARRAY_COUNT(sSideStatusListItems);
break;
case LIST_ITEM_INSTANT_WIN:
case LIST_ITEM_AI_MOVES_PTS:
case LIST_ITEM_AI_INFO:
return;
}
data->secondaryListItemCount = itemsCount;
data->secondaryListWindowId = AddWindow(&winTemplate);
listTemplate.totalItems = itemsCount;
listTemplate.maxShowed = itemsCount;
if (listTemplate.maxShowed > 7 && !sHasChangeableEntries[data->currentMainListItemId])
listTemplate.maxShowed = 7;
listTemplate.windowId = data->secondaryListWindowId;
data->secondaryListTaskId = ListMenuInit(&listTemplate, 0, 0);
CopyWindowToVram(data->secondaryListWindowId, COPYWIN_FULL);
}
static void PadString(const u8 *src, u8 *dst)
{
u32 i;
for (i = 0; i < 19 && src[i] != EOS; i++)
dst[i] = src[i];
for (; i < 19; i++)
dst[i] = CHAR_SPACE;
dst[i] = EOS;
}
static const u8 sTextAll[] = _("All");
static void PrintSecondaryEntries(struct BattleDebugMenu *data)
{
u8 text[20];
s32 i;
struct TextPrinterTemplate printer;
u8 yMultiplier;
// Do not print entries if they are not changing.
if (!sHasChangeableEntries[data->currentMainListItemId])
return;
yMultiplier = (GetFontAttribute(sSecondaryListTemplate.fontId, 1) + sSecondaryListTemplate.itemVerticalPadding);
printer.windowId = data->secondaryListWindowId;
printer.fontId = 1;
printer.unk = 0;
printer.letterSpacing = 0;
printer.lineSpacing = 1;
printer.fgColor = 2;
printer.bgColor = 1;
printer.shadowColor = 3;
printer.x = sSecondaryListTemplate.item_X;
printer.currentX = sSecondaryListTemplate.item_X;
printer.currentChar = text;
switch (data->currentMainListItemId)
{
case LIST_ITEM_MOVES:
case LIST_ITEM_PP:
for (i = 0; i < 4; i++)
{
PadString(GetMoveName(gBattleMons[data->battlerId].moves[i]), text);
printer.currentY = printer.y = (i * yMultiplier) + sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
}
// Allow changing all moves at once. Useful for testing in wild doubles.
if (data->currentMainListItemId == LIST_ITEM_MOVES)
{
PadString(sTextAll, text);
printer.currentY = printer.y = (i * yMultiplier) + sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
}
break;
case LIST_ITEM_ABILITY:
PadString(gAbilitiesInfo[gBattleMons[data->battlerId].ability].name, text);
printer.currentY = printer.y = sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
break;
case LIST_ITEM_HELD_ITEM:
PadString(GetItemName(gBattleMons[data->battlerId].item), text);
printer.currentY = printer.y = sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
break;
case LIST_ITEM_TYPES:
for (i = 0; i < 3; i++)
{
u8 *types = &gBattleMons[data->battlerId].types[0];
PadString(gTypesInfo[types[i]].name, text);
printer.currentY = printer.y = (i * yMultiplier) + sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
}
break;
case LIST_ITEM_STAT_STAGES:
for (i = 0; i < NUM_BATTLE_STATS - 1; i++)
{
u8 *txtPtr = StringCopy(text, gStatNamesTable[STAT_ATK + i]);
txtPtr[0] = CHAR_SPACE;
if (gBattleMons[data->battlerId].statStages[STAT_ATK + i] >= DEFAULT_STAT_STAGE)
{
txtPtr[1] = CHAR_PLUS;
txtPtr[2] = CHAR_0 + (gBattleMons[data->battlerId].statStages[STAT_ATK + i] - DEFAULT_STAT_STAGE);
}
else
{
txtPtr[1] = CHAR_HYPHEN;
txtPtr[2] = CHAR_6 - (gBattleMons[data->battlerId].statStages[STAT_ATK + i]);
}
txtPtr[3] = EOS;
PadString(text, text);
printer.currentY = printer.y = (i * yMultiplier) + sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
}
// Allow changing all stat stages at once.
PadString(sTextAll, text);
printer.currentY = printer.y = (i * yMultiplier) + sSecondaryListTemplate.upText_Y;
AddTextPrinter(&printer, 0, NULL);
break;
}
}
static void DestroyModifyArrows(struct BattleDebugMenu *data)
{
if (data->modifyArrows.arrowSpriteId[0] != 0xFF)
DestroySpriteAndFreeResources(&gSprites[data->modifyArrows.arrowSpriteId[0]]);
if (data->modifyArrows.arrowSpriteId[1] != 0xFF)
DestroySpriteAndFreeResources(&gSprites[data->modifyArrows.arrowSpriteId[1]]);
}
static void PrintDigitChars(struct BattleDebugMenu *data)
{
s32 i;
u8 text[MAX_MODIFY_DIGITS + 1];
for (i = 0; i < data->modifyArrows.maxDigits; i++)
text[i] = data->modifyArrows.charDigits[i];
text[i] = EOS;
FillWindowPixelBuffer(data->modifyWindowId, 0x11);
AddTextPrinterParameterized(data->modifyWindowId, FONT_NORMAL, text, 3, 0, 0, NULL);
}
static const u32 GetBitfieldToAndValue(u32 currBit, u32 bitsCount)
{
u32 i;
u32 toAnd = 0;
for (i = 0; i < bitsCount; i++)
toAnd |= (1 << (currBit + i));
return toAnd;
}
static const u32 GetBitfieldValue(u32 value, u32 currBit, u32 bitsCount)
{
return (value & (GetBitfieldToAndValue(currBit, bitsCount))) >> currBit;
}
static void UpdateBattlerValue(struct BattleDebugMenu *data)
{
u32 i;
switch (data->modifyArrows.typeOfVal)
{
case VAL_U8:
*(u8 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
break;
case VAL_S8:
*(s8 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
break;
case VAL_U16:
*(u16 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
break;
case VAR_U16_4_ENTRIES:
((u16 *)(data->modifyArrows.modifiedValPtr))[0] = data->modifyArrows.currValue;
((u16 *)(data->modifyArrows.modifiedValPtr))[1] = data->modifyArrows.currValue;
((u16 *)(data->modifyArrows.modifiedValPtr))[2] = data->modifyArrows.currValue;
((u16 *)(data->modifyArrows.modifiedValPtr))[3] = data->modifyArrows.currValue;
break;
case VAL_ALL_STAT_STAGES:
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[data->battlerId].statStages[i] = data->modifyArrows.currValue;
break;
case VAL_U32:
*(u32 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
break;
case VAL_BITFIELD_32:
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~(GetBitfieldToAndValue(data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount));
*(u32 *)(data->modifyArrows.modifiedValPtr) |= (data->modifyArrows.currValue << data->bitfield[data->currentSecondaryListItemId].currBit);
break;
case VAL_VOLATILE:
SetMonVolatile(data->battlerId, data->currentSecondaryListItemId, data->modifyArrows.currValue);
break;
case VAL_HAZARDS:
ChangeHazardsValue(data);
break;
case VAR_SIDE_STATUS:
*GetSideStatusValue(data, TRUE, data->modifyArrows.currValue != 0) = data->modifyArrows.currValue;
break;
case VAR_SHOW_HP:
(*(struct BattleSpriteInfo*)(data->modifyArrows.modifiedValPtr)).hpNumbersNoBars = data->modifyArrows.currValue;
break;
case VAR_SUBSTITUTE:
*(u8 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
if (*(u8 *)(data->modifyArrows.modifiedValPtr) == 0)
{
gBattleMons[data->battlerId].volatiles.substitute = FALSE;
gBattleSpritesDataPtr->battlerData[data->battlerId].behindSubstitute = 0;
}
else
{
gBattleMons[data->battlerId].volatiles.substitute = TRUE;
gBattleSpritesDataPtr->battlerData[data->battlerId].behindSubstitute = 1;
}
break;
case VAR_IN_LOVE:
if (data->modifyArrows.currValue)
{
if (IsBattlerAlive(BATTLE_OPPOSITE(data->battlerId)))
gBattleMons[data->battlerId].volatiles.infatuation = INFATUATED_WITH(BATTLE_OPPOSITE(data->battlerId));
else
gBattleMons[data->battlerId].volatiles.infatuation = INFATUATED_WITH(BATTLE_PARTNER(BATTLE_OPPOSITE(data->battlerId)));
}
else
{
gBattleMons[data->battlerId].volatiles.infatuation = 0;
}
break;
}
data->battlerWasChanged[data->battlerId] = TRUE;
}
static u32 CharDigitsToValue(u8 *charDigits, u8 maxDigits)
{
s32 i;
u8 id = 0;
u32 newValue = 0;
u8 valueDigits[MAX_MODIFY_DIGITS];
for (i = 0; i < MAX_MODIFY_DIGITS; i++)
valueDigits[i] = charDigits[i] - CHAR_0;
if (maxDigits >= MAX_MODIFY_DIGITS)
newValue += valueDigits[id++] * 1000;
if (maxDigits >= MAX_MODIFY_DIGITS - 1)
newValue += valueDigits[id++] * 100;
if (maxDigits >= MAX_MODIFY_DIGITS - 2)
newValue += valueDigits[id++] * 10;
if (maxDigits >= MAX_MODIFY_DIGITS - 3)
newValue += valueDigits[id++];
return newValue;
}
static void ValueToCharDigits(u8 *charDigits, u32 newValue, u8 maxDigits)
{
s32 i;
u8 valueDigits[MAX_MODIFY_DIGITS];
u8 id = 0;
if (maxDigits >= MAX_MODIFY_DIGITS)
valueDigits[id++] = newValue / 1000;
if (maxDigits >= MAX_MODIFY_DIGITS - 1)
valueDigits[id++] = (newValue % 1000) / 100;
if (maxDigits >= MAX_MODIFY_DIGITS - 2)
valueDigits[id++] = (newValue % 100) / 10;
if (maxDigits >= MAX_MODIFY_DIGITS - 3)
valueDigits[id++] = newValue % 10;
for (i = 0; i < MAX_MODIFY_DIGITS; i++)
charDigits[i] = valueDigits[i] + CHAR_0;
}
static void ChangeHazardsValue(struct BattleDebugMenu *data)
{
u32 side = GetBattlerSide(data->battlerId);
switch (data->currentSecondaryListItemId)
{
case LIST_SIDE_SPIKES:
if (data->modifyArrows.currValue > 0)
{
if (gSideTimers[side].spikesAmount == 0)
PushHazardTypeToQueue(side, HAZARDS_SPIKES);
gSideTimers[side].spikesAmount = data->modifyArrows.currValue;
}
else if (data->modifyArrows.currValue == 0)
{
gSideTimers[side].spikesAmount = 0;
RemoveHazardFromField(side, HAZARDS_SPIKES);
}
break;
case LIST_SIDE_TOXIC_SPIKES:
if (data->modifyArrows.currValue > 0)
{
if (gSideTimers[side].toxicSpikesAmount == 0)
PushHazardTypeToQueue(side, HAZARDS_TOXIC_SPIKES);
gSideTimers[side].toxicSpikesAmount = data->modifyArrows.currValue;
}
else if (data->modifyArrows.currValue == 0)
{
gSideTimers[side].toxicSpikesAmount = 0;
RemoveHazardFromField(side, HAZARDS_TOXIC_SPIKES);
}
break;
case LIST_SIDE_STICKY_WEB:
if (data->modifyArrows.currValue > 0)
PushHazardTypeToQueue(side, HAZARDS_STICKY_WEB);
else if (data->modifyArrows.currValue == 0)
RemoveHazardFromField(side, HAZARDS_STICKY_WEB);
break;
case LIST_SIDE_STEALTH_ROCK:
if (data->modifyArrows.currValue > 0)
PushHazardTypeToQueue(side, HAZARDS_STEALTH_ROCK);
else if (data->modifyArrows.currValue == 0)
RemoveHazardFromField(side, HAZARDS_STEALTH_ROCK);
break;
case LIST_SIDE_STEELSURGE:
if (data->modifyArrows.currValue > 0)
PushHazardTypeToQueue(side, HAZARDS_STEELSURGE);
else if (data->modifyArrows.currValue == 0)
RemoveHazardFromField(side, HAZARDS_STEELSURGE);
break;
}
}
static u32 GetHazardsValue(struct BattleDebugMenu *data)
{
u32 hazardsLayers = 0;
switch (data->currentSecondaryListItemId)
{
case LIST_SIDE_SPIKES:
hazardsLayers = gSideTimers[GetBattlerSide(data->battlerId)].spikesAmount;
break;
case LIST_SIDE_TOXIC_SPIKES:
hazardsLayers = gSideTimers[GetBattlerSide(data->battlerId)].toxicSpikesAmount;
break;
case LIST_SIDE_STICKY_WEB:
hazardsLayers = IsHazardOnSide(GetBattlerSide(data->battlerId), HAZARDS_STICKY_WEB);
break;
case LIST_SIDE_STEALTH_ROCK:
hazardsLayers = IsHazardOnSide(GetBattlerSide(data->battlerId), HAZARDS_STEALTH_ROCK);
break;
case LIST_SIDE_STEELSURGE:
hazardsLayers = IsHazardOnSide(GetBattlerSide(data->battlerId), HAZARDS_STEELSURGE);
break;
}
return hazardsLayers;
}
static u16 *GetSideStatusValue(struct BattleDebugMenu *data, bool32 changeStatus, bool32 statusTrue)
{
struct SideTimer *sideTimer = &gSideTimers[GetBattlerSide(data->battlerId)];
switch (data->currentSecondaryListItemId)
{
case LIST_SIDE_REFLECT:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_REFLECT;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_REFLECT;
}
return &sideTimer->reflectTimer;
case LIST_SIDE_LIGHTSCREEN:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_LIGHTSCREEN;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_LIGHTSCREEN;
}
return &sideTimer->lightscreenTimer;
case LIST_SIDE_SAFEGUARD:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_SAFEGUARD;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_SAFEGUARD;
}
return &sideTimer->safeguardTimer;
case LIST_SIDE_MIST:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_MIST;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_MIST;
}
return &sideTimer->mistTimer;
case LIST_SIDE_TAILWIND:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_TAILWIND;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_TAILWIND;
}
return &sideTimer->tailwindTimer;
case LIST_SIDE_AURORA_VEIL:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_AURORA_VEIL;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_AURORA_VEIL;
}
return &sideTimer->auroraVeilTimer;
case LIST_SIDE_LUCKY_CHANT:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_LUCKY_CHANT;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_LUCKY_CHANT;
}
return &sideTimer->luckyChantTimer;
case LIST_SIDE_DAMAGE_NON_TYPES:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_DAMAGE_NON_TYPES;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_DAMAGE_NON_TYPES;
sideTimer->damageNonTypesType = GetMoveType(gCurrentMove);
}
return &sideTimer->damageNonTypesTimer;
case LIST_SIDE_RAINBOW:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_RAINBOW;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_RAINBOW;
}
return &sideTimer->rainbowTimer;
case LIST_SIDE_SEA_OF_FIRE:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_SEA_OF_FIRE;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_SEA_OF_FIRE;
}
return &sideTimer->seaOfFireTimer;
case LIST_SIDE_SWAMP:
if (changeStatus)
{
if (statusTrue)
*(u32 *)(data->modifyArrows.modifiedValPtr) |= SIDE_STATUS_SWAMP;
else
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~SIDE_STATUS_SWAMP;
}
return &sideTimer->swampTimer;
default:
return NULL;
}
}
static void SetUpModifyArrows(struct BattleDebugMenu *data)
{
LoadSpritePalette(&gSpritePalette_Arrow);
data->modifyArrows.arrowSpriteId[0] = CreateSprite(&gSpriteTemplate_Arrow, 207, 12, 0);
data->modifyArrows.arrowSpriteId[1] = CreateSprite(&gSpriteTemplate_Arrow, 207, 36, 0);
gSprites[data->modifyArrows.arrowSpriteId[1]].animNum = 1;
switch (data->currentMainListItemId)
{
case LIST_ITEM_ABILITY:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = ABILITIES_COUNT - 1;
data->modifyArrows.maxDigits = 3;
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].ability;
data->modifyArrows.typeOfVal = VAL_U16;
data->modifyArrows.currValue = gBattleMons[data->battlerId].ability;
break;
case LIST_ITEM_MOVES:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = MOVES_COUNT - 1;
data->modifyArrows.maxDigits = 3;
if (data->currentSecondaryListItemId == 4)
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].moves[0];
data->modifyArrows.currValue = gBattleMons[data->battlerId].moves[0];
data->modifyArrows.typeOfVal = VAR_U16_4_ENTRIES;
}
else
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].moves[data->currentSecondaryListItemId];
data->modifyArrows.currValue = gBattleMons[data->battlerId].moves[data->currentSecondaryListItemId];
data->modifyArrows.typeOfVal = VAL_U16;
}
break;
case LIST_ITEM_PP:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = CalculatePPWithBonus(gBattleMons[data->battlerId].moves[data->currentSecondaryListItemId], gBattleMons[data->battlerId].ppBonuses, data->currentSecondaryListItemId);
data->modifyArrows.maxDigits = 2;
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].pp[data->currentSecondaryListItemId];
data->modifyArrows.typeOfVal = VAL_U8;
data->modifyArrows.currValue = gBattleMons[data->battlerId].pp[data->currentSecondaryListItemId];
break;
case LIST_ITEM_HELD_ITEM:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = ITEMS_COUNT - 1;
data->modifyArrows.maxDigits = 3;
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].item;
data->modifyArrows.typeOfVal = VAL_U16;
data->modifyArrows.currValue = gBattleMons[data->battlerId].item;
break;
case LIST_ITEM_TYPES:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = NUMBER_OF_MON_TYPES - 1;
data->modifyArrows.maxDigits = 2;
data->modifyArrows.modifiedValPtr = (u8 *)((&gBattleMons[data->battlerId].types[0]) + data->currentSecondaryListItemId);
data->modifyArrows.typeOfVal = VAL_U8;
data->modifyArrows.currValue = *(u8 *)((&gBattleMons[data->battlerId].types[0]) + data->currentSecondaryListItemId);
break;
case LIST_ITEM_STATS:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 9999;
data->modifyArrows.maxDigits = 4;
data->modifyArrows.typeOfVal = VAL_U16;
if (data->currentSecondaryListItemId == LIST_STAT_HP_CURRENT)
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].hp;
data->modifyArrows.currValue = gBattleMons[data->battlerId].hp;
data->modifyArrows.minValue = 1;
data->modifyArrows.maxValue = gBattleMons[data->battlerId].maxHP;
}
else if (data->currentSecondaryListItemId == LIST_STAT_HP_MAX)
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].maxHP;
data->modifyArrows.minValue = gBattleMons[data->battlerId].hp;
data->modifyArrows.currValue = gBattleMons[data->battlerId].maxHP;
}
else
{
data->modifyArrows.modifiedValPtr = (u16 *)((&gBattleMons[data->battlerId].attack) + (data->currentSecondaryListItemId - 2));
data->modifyArrows.currValue = *(u16 *)((&gBattleMons[data->battlerId].attack) + (data->currentSecondaryListItemId - 2));
}
break;
case LIST_ITEM_STAT_STAGES:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 12;
data->modifyArrows.maxDigits = 2;
if (data->currentSecondaryListItemId == NUM_BATTLE_STATS - 1) // Change all stats
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].statStages[STAT_ATK];
data->modifyArrows.currValue = gBattleMons[data->battlerId].statStages[STAT_ATK];
data->modifyArrows.typeOfVal = VAL_ALL_STAT_STAGES;
}
else
{
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].statStages[data->currentSecondaryListItemId + STAT_ATK];
data->modifyArrows.typeOfVal = VAL_U8;
data->modifyArrows.currValue = gBattleMons[data->battlerId].statStages[data->currentSecondaryListItemId + STAT_ATK];
}
break;
case LIST_ITEM_VARIOUS:
if (data->currentSecondaryListItemId == VARIOUS_SHOW_HP)
{
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 1;
data->modifyArrows.maxDigits = 1;
data->modifyArrows.modifiedValPtr = &gBattleSpritesDataPtr->battlerData[data->battlerId];
data->modifyArrows.typeOfVal = VAR_SHOW_HP;
data->modifyArrows.currValue = gBattleSpritesDataPtr->battlerData[data->battlerId].hpNumbersNoBars;
}
else if (data->currentSecondaryListItemId == VARIOUS_SUBSTITUTE_HP)
{
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 255;
data->modifyArrows.maxDigits = 3;
data->modifyArrows.modifiedValPtr = &gDisableStructs[data->battlerId].substituteHP;
data->modifyArrows.typeOfVal = VAR_SUBSTITUTE;
data->modifyArrows.currValue = gDisableStructs[data->battlerId].substituteHP;
}
else if (data->currentSecondaryListItemId == VARIOUS_IN_LOVE)
{
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 1;
data->modifyArrows.maxDigits = 1;
data->modifyArrows.modifiedValPtr = NULL;
data->modifyArrows.typeOfVal = VAR_IN_LOVE;
data->modifyArrows.currValue = gBattleMons[data->battlerId].volatiles.infatuation;
}
break;
case LIST_ITEM_STATUS1:
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].status1;
data->modifyArrows.currValue = GetBitfieldValue(gBattleMons[data->battlerId].status1, data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
case LIST_ITEM_VOLATILE:
data->modifyArrows.currValue = GetBattlerVolatile(data->battlerId, data->currentSecondaryListItemId);
data->modifyArrows.typeOfVal = VAL_VOLATILE;
data->modifyArrows.minValue = 0;
#define UNPACK_VOLATILE_MAX_SIZE(_enum, _fieldName, _typeMaxValue, ...) case _enum: data->modifyArrows.maxValue = min(MAX_u16, GET_VOLATILE_MAXIMUM(_typeMaxValue)); break;
switch (data->currentSecondaryListItemId)
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_MAX_SIZE)
/* Expands to the following:
* case VOLATILE_CONFUSION:
data->modifyArrows.maxValue = MAX_BITS(3); // Max value 7
break;
* case VOLATILE_FLINCHED:
data->modifyArrows.maxValue = MAX_BITS(1); // Max value 1
break;
* ...etc.
*/
default:
data->modifyArrows.maxValue = 0;
}
data->modifyArrows.maxDigits = MAX_DIGITS(data->modifyArrows.maxValue);
break;
case LIST_ITEM_AI:
data->modifyArrows.modifiedValPtr = &gAiThinkingStruct->aiFlags[data->battlerId];
data->modifyArrows.currValue = GetBitfieldValue(gAiThinkingStruct->aiFlags[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
CASE_ITEM_STATUS:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = (1 << data->bitfield[data->currentSecondaryListItemId].bitsCount) - 1;
data->modifyArrows.maxDigits = MAX_DIGITS(data->modifyArrows.maxValue);
break;
case LIST_ITEM_HAZARDS:
data->modifyArrows.minValue = 0;
switch (data->currentSecondaryListItemId)
{
case LIST_SIDE_SPIKES:
data->modifyArrows.maxValue = 3;
break;
case LIST_SIDE_TOXIC_SPIKES:
data->modifyArrows.maxValue = 2;
break;
case LIST_SIDE_STICKY_WEB:
case LIST_SIDE_STEALTH_ROCK:
case LIST_SIDE_STEELSURGE:
data->modifyArrows.maxValue = 1;
break;
}
data->modifyArrows.maxDigits = 2;
data->modifyArrows.typeOfVal = VAL_HAZARDS;
data->modifyArrows.currValue = GetHazardsValue(data);
break;
case LIST_ITEM_SIDE_STATUS:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = 9;
data->modifyArrows.maxDigits = 2;
data->modifyArrows.modifiedValPtr = &gSideStatuses[GetBattlerSide(data->battlerId)];
data->modifyArrows.typeOfVal = VAR_SIDE_STATUS;
data->modifyArrows.currValue = *GetSideStatusValue(data, FALSE, FALSE);
break;
}
data->modifyArrows.currentDigit = 0;
ValueToCharDigits(data->modifyArrows.charDigits, data->modifyArrows.currValue, data->modifyArrows.maxDigits);
}
static bool32 TryMoveDigit(struct BattleDebugModifyArrows *modArrows, bool32 moveUp)
{
s32 i;
u8 charDigits[MAX_MODIFY_DIGITS];
u32 newValue;
for (i = 0; i < MAX_MODIFY_DIGITS; i++)
charDigits[i] = modArrows->charDigits[i];
if (moveUp)
{
if (charDigits[modArrows->currentDigit] == CHAR_9)
{
charDigits[modArrows->currentDigit] = CHAR_0;
for (i = modArrows->currentDigit - 1; i >= 0; i--)
{
if (charDigits[i] == CHAR_9)
{
charDigits[i] = CHAR_0;
}
else
{
charDigits[i]++;
break;
}
}
}
else
charDigits[modArrows->currentDigit]++;
}
else
{
if (charDigits[modArrows->currentDigit] == CHAR_0)
{
charDigits[modArrows->currentDigit] = CHAR_9;
for (i = modArrows->currentDigit - 1; i >= 0; i--)
{
if (charDigits[i] == CHAR_0)
{
charDigits[i] = CHAR_9;
}
else
{
charDigits[i]--;
break;
}
}
}
else
charDigits[modArrows->currentDigit]--;
}
newValue = CharDigitsToValue(charDigits, modArrows->maxDigits);
if (newValue > modArrows->maxValue || newValue < modArrows->minValue)
{
return FALSE;
}
else
{
modArrows->currValue = newValue;
for (i = 0; i < MAX_MODIFY_DIGITS; i++)
modArrows->charDigits[i] = charDigits[i];
return TRUE;
}
}
static void UpdateMonData(struct BattleDebugMenu *data)
{
s32 i, j;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (data->battlerWasChanged[i])
{
struct Pokemon *mon = GetBattlerMon(i);
struct BattlePokemon *battleMon = &gBattleMons[i];
SetMonData(mon, MON_DATA_HELD_ITEM, &battleMon->item);
SetMonData(mon, MON_DATA_STATUS, &battleMon->status1);
SetMonData(mon, MON_DATA_HP, &battleMon->hp);
SetMonData(mon, MON_DATA_MAX_HP, &battleMon->maxHP);
for (j = 0; j < 4; j++)
SetMonData(mon, MON_DATA_MOVE1 + j, &battleMon->moves[j]);
}
}
}
static const u8 *const sHoldEffectNames[HOLD_EFFECT_COUNT] =
{
[HOLD_EFFECT_NONE] = COMPOUND_STRING("????????"),
[HOLD_EFFECT_RESTORE_HP] = COMPOUND_STRING("Restore Hp"),
[HOLD_EFFECT_CURE_PAR] = COMPOUND_STRING("Cure Par"),
[HOLD_EFFECT_CURE_SLP] = COMPOUND_STRING("Cure Slp"),
[HOLD_EFFECT_CURE_PSN] = COMPOUND_STRING("Cure Psn"),
[HOLD_EFFECT_CURE_BRN] = COMPOUND_STRING("Cure Brn"),
[HOLD_EFFECT_CURE_FRZ] = COMPOUND_STRING("Cure Frz"),
[HOLD_EFFECT_RESTORE_PP] = COMPOUND_STRING("Restore Pp"),
[HOLD_EFFECT_CURE_CONFUSION] = COMPOUND_STRING("Cure Confusion"),
[HOLD_EFFECT_CURE_STATUS] = COMPOUND_STRING("Cure Status"),
[HOLD_EFFECT_CONFUSE_SPICY] = COMPOUND_STRING("Confuse Spicy"),
[HOLD_EFFECT_CONFUSE_DRY] = COMPOUND_STRING("Confuse Dry"),
[HOLD_EFFECT_CONFUSE_SWEET] = COMPOUND_STRING("Confuse Sweet"),
[HOLD_EFFECT_CONFUSE_BITTER] = COMPOUND_STRING("Confuse Bitter"),
[HOLD_EFFECT_CONFUSE_SOUR] = COMPOUND_STRING("Confuse Sour"),
[HOLD_EFFECT_ATTACK_UP] = COMPOUND_STRING("Attack Up"),
[HOLD_EFFECT_DEFENSE_UP] = COMPOUND_STRING("Defense Up"),
[HOLD_EFFECT_SPEED_UP] = COMPOUND_STRING("Speed Up"),
[HOLD_EFFECT_SP_ATTACK_UP] = COMPOUND_STRING("Sp Attack Up"),
[HOLD_EFFECT_SP_DEFENSE_UP] = COMPOUND_STRING("Sp Defense Up"),
[HOLD_EFFECT_CRITICAL_UP] = COMPOUND_STRING("Critical Up"),
[HOLD_EFFECT_RANDOM_STAT_UP] = COMPOUND_STRING("Random Stat Up"),
[HOLD_EFFECT_EVASION_UP] = COMPOUND_STRING("Evasion Up"),
[HOLD_EFFECT_WHITE_HERB] = COMPOUND_STRING("Restore Stats"),
[HOLD_EFFECT_MACHO_BRACE] = COMPOUND_STRING("Macho Brace"),
[HOLD_EFFECT_EXP_SHARE] = COMPOUND_STRING("Exp Share"),
[HOLD_EFFECT_QUICK_CLAW] = COMPOUND_STRING("Quick Claw"),
[HOLD_EFFECT_FRIENDSHIP_UP] = COMPOUND_STRING("Friendship Up"),
[HOLD_EFFECT_MENTAL_HERB] = COMPOUND_STRING("Mental Herb"),
[HOLD_EFFECT_CHOICE_BAND] = COMPOUND_STRING("Choice Band"),
[HOLD_EFFECT_FLINCH] = COMPOUND_STRING("Flinch"),
[HOLD_EFFECT_DOUBLE_PRIZE] = COMPOUND_STRING("Double Prize"),
[HOLD_EFFECT_REPEL] = COMPOUND_STRING("Repel"),
[HOLD_EFFECT_SOUL_DEW] = COMPOUND_STRING("Soul Dew"),
[HOLD_EFFECT_DEEP_SEA_TOOTH] = COMPOUND_STRING("Deep Sea Tooth"),
[HOLD_EFFECT_DEEP_SEA_SCALE] = COMPOUND_STRING("Deep Sea Scale"),
[HOLD_EFFECT_CAN_ALWAYS_RUN] = COMPOUND_STRING("Can Always Run"),
[HOLD_EFFECT_PREVENT_EVOLVE] = COMPOUND_STRING("Prevent Evolve"),
[HOLD_EFFECT_FOCUS_BAND] = COMPOUND_STRING("Focus Band"),
[HOLD_EFFECT_LUCKY_EGG] = COMPOUND_STRING("Lucky Egg"),
[HOLD_EFFECT_SCOPE_LENS] = COMPOUND_STRING("Scope Lens"),
[HOLD_EFFECT_LEFTOVERS] = COMPOUND_STRING("Leftovers"),
[HOLD_EFFECT_DRAGON_SCALE] = COMPOUND_STRING("Dragon Scale"),
[HOLD_EFFECT_LIGHT_BALL] = COMPOUND_STRING("Light Ball"),
[HOLD_EFFECT_TYPE_POWER] = COMPOUND_STRING("Type Power"),
[HOLD_EFFECT_UPGRADE] = COMPOUND_STRING("Upgrade"),
[HOLD_EFFECT_SHELL_BELL] = COMPOUND_STRING("Shell Bell"),
[HOLD_EFFECT_LUCKY_PUNCH] = COMPOUND_STRING("Lucky Punch"),
[HOLD_EFFECT_METAL_POWDER] = COMPOUND_STRING("Metal Powder"),
[HOLD_EFFECT_THICK_CLUB] = COMPOUND_STRING("Thick Club"),
[HOLD_EFFECT_LEEK] = COMPOUND_STRING("Leek"),
[HOLD_EFFECT_CHOICE_SCARF] = COMPOUND_STRING("Choice Scarf"),
[HOLD_EFFECT_CHOICE_SPECS] = COMPOUND_STRING("Choice Specs"),
[HOLD_EFFECT_DAMP_ROCK] = COMPOUND_STRING("Damp Rock"),
[HOLD_EFFECT_GRIP_CLAW] = COMPOUND_STRING("Grip Claw"),
[HOLD_EFFECT_HEAT_ROCK] = COMPOUND_STRING("Heat Rock"),
[HOLD_EFFECT_ICY_ROCK] = COMPOUND_STRING("Icy Rock"),
[HOLD_EFFECT_LIGHT_CLAY] = COMPOUND_STRING("Light Clay"),
[HOLD_EFFECT_SMOOTH_ROCK] = COMPOUND_STRING("Smooth Rock"),
[HOLD_EFFECT_POWER_HERB] = COMPOUND_STRING("Power Herb"),
[HOLD_EFFECT_BIG_ROOT] = COMPOUND_STRING("Big Root"),
[HOLD_EFFECT_EXPERT_BELT] = COMPOUND_STRING("Expert Belt"),
[HOLD_EFFECT_LIFE_ORB] = COMPOUND_STRING("Life Orb"),
[HOLD_EFFECT_METRONOME] = COMPOUND_STRING("Metronome"),
[HOLD_EFFECT_MUSCLE_BAND] = COMPOUND_STRING("Muscle Band"),
[HOLD_EFFECT_WIDE_LENS] = COMPOUND_STRING("Wide Lens"),
[HOLD_EFFECT_WISE_GLASSES] = COMPOUND_STRING("Wise Glasses"),
[HOLD_EFFECT_ZOOM_LENS] = COMPOUND_STRING("Zoom Lens"),
[HOLD_EFFECT_LAGGING_TAIL] = COMPOUND_STRING("Lagging Tail"),
[HOLD_EFFECT_FOCUS_SASH] = COMPOUND_STRING("Focus Sash"),
[HOLD_EFFECT_FLAME_ORB] = COMPOUND_STRING("Flame Orb"),
[HOLD_EFFECT_TOXIC_ORB] = COMPOUND_STRING("Toxic Orb"),
[HOLD_EFFECT_STICKY_BARB] = COMPOUND_STRING("Sticky Barb"),
[HOLD_EFFECT_IRON_BALL] = COMPOUND_STRING("Iron Ball"),
[HOLD_EFFECT_BLACK_SLUDGE] = COMPOUND_STRING("Black Sludge"),
[HOLD_EFFECT_DESTINY_KNOT] = COMPOUND_STRING("Destiny Knot"),
[HOLD_EFFECT_SHED_SHELL] = COMPOUND_STRING("Shed Shell"),
[HOLD_EFFECT_QUICK_POWDER] = COMPOUND_STRING("Quick Powder"),
[HOLD_EFFECT_ADAMANT_ORB] = COMPOUND_STRING("Adamant Orb"),
[HOLD_EFFECT_LUSTROUS_ORB] = COMPOUND_STRING("Lustrous Orb"),
[HOLD_EFFECT_GRISEOUS_ORB] = COMPOUND_STRING("Griseous Orb"),
[HOLD_EFFECT_ENIGMA_BERRY] = COMPOUND_STRING("Enigma Berry"),
[HOLD_EFFECT_RESIST_BERRY] = COMPOUND_STRING("Resist Berry"),
[HOLD_EFFECT_POWER_ITEM] = COMPOUND_STRING("Power Item"),
[HOLD_EFFECT_RESTORE_PCT_HP] = COMPOUND_STRING("Restore Pct Hp"),
[HOLD_EFFECT_MICLE_BERRY] = COMPOUND_STRING("Micle Berry"),
[HOLD_EFFECT_CUSTAP_BERRY] = COMPOUND_STRING("Custap Berry"),
[HOLD_EFFECT_JABOCA_BERRY] = COMPOUND_STRING("Jaboca Berry"),
[HOLD_EFFECT_ROWAP_BERRY] = COMPOUND_STRING("Rowap Berry"),
[HOLD_EFFECT_KEE_BERRY] = COMPOUND_STRING("Kee Berry"),
[HOLD_EFFECT_MARANGA_BERRY] = COMPOUND_STRING("Maranga Berry"),
[HOLD_EFFECT_PLATE] = COMPOUND_STRING("Plate"),
[HOLD_EFFECT_FLOAT_STONE] = COMPOUND_STRING("Float Stone"),
[HOLD_EFFECT_EVIOLITE] = COMPOUND_STRING("Eviolite"),
[HOLD_EFFECT_ASSAULT_VEST] = COMPOUND_STRING("Assault Vest"),
[HOLD_EFFECT_DRIVE] = COMPOUND_STRING("Drive"),
[HOLD_EFFECT_GEMS] = COMPOUND_STRING("Gems"),
[HOLD_EFFECT_ROCKY_HELMET] = COMPOUND_STRING("Rocky Helmet"),
[HOLD_EFFECT_AIR_BALLOON] = COMPOUND_STRING("Air Balloon"),
[HOLD_EFFECT_RED_CARD] = COMPOUND_STRING("Red Card"),
[HOLD_EFFECT_RING_TARGET] = COMPOUND_STRING("Ring Target"),
[HOLD_EFFECT_BINDING_BAND] = COMPOUND_STRING("Binding Band"),
[HOLD_EFFECT_EJECT_BUTTON] = COMPOUND_STRING("Eject Button"),
[HOLD_EFFECT_ABSORB_BULB] = COMPOUND_STRING("Absorb Bulb"),
[HOLD_EFFECT_CELL_BATTERY] = COMPOUND_STRING("Cell Battery"),
[HOLD_EFFECT_MEGA_STONE] = COMPOUND_STRING("Mega Stone"),
[HOLD_EFFECT_SAFETY_GOGGLES] = COMPOUND_STRING("Safety Goggles"),
[HOLD_EFFECT_LUMINOUS_MOSS] = COMPOUND_STRING("Luminous Moss"),
[HOLD_EFFECT_SNOWBALL] = COMPOUND_STRING("Snowball"),
[HOLD_EFFECT_WEAKNESS_POLICY] = COMPOUND_STRING("Weakness Policy"),
[HOLD_EFFECT_PRIMAL_ORB] = COMPOUND_STRING("Primal Orb"),
[HOLD_EFFECT_PROTECTIVE_PADS] = COMPOUND_STRING("Protective Pads"),
[HOLD_EFFECT_TERRAIN_EXTENDER] = COMPOUND_STRING("Terrain Extender"),
[HOLD_EFFECT_SEEDS] = COMPOUND_STRING("Seeds"),
[HOLD_EFFECT_ADRENALINE_ORB] = COMPOUND_STRING("Adrenaline Orb"),
[HOLD_EFFECT_MEMORY] = COMPOUND_STRING("Memory"),
[HOLD_EFFECT_Z_CRYSTAL] = COMPOUND_STRING("Z-Crystal"),
[HOLD_EFFECT_UTILITY_UMBRELLA] = COMPOUND_STRING("Utility Umbrella"),
[HOLD_EFFECT_EJECT_PACK] = COMPOUND_STRING("Eject Pack"),
[HOLD_EFFECT_ROOM_SERVICE] = COMPOUND_STRING("Room Service"),
[HOLD_EFFECT_BLUNDER_POLICY] = COMPOUND_STRING("Blunder Policy"),
[HOLD_EFFECT_HEAVY_DUTY_BOOTS] = COMPOUND_STRING("Heavy Duty Boots"),
[HOLD_EFFECT_THROAT_SPRAY] = COMPOUND_STRING("Throat Spray"),
[HOLD_EFFECT_ABILITY_SHIELD] = COMPOUND_STRING("Ability Shield"),
[HOLD_EFFECT_CLEAR_AMULET] = COMPOUND_STRING("Clear Amulet"),
[HOLD_EFFECT_MIRROR_HERB] = COMPOUND_STRING("Mirror Herb"),
[HOLD_EFFECT_PUNCHING_GLOVE] = COMPOUND_STRING("Punching Glove"),
[HOLD_EFFECT_COVERT_CLOAK] = COMPOUND_STRING("Covert Cloak"),
[HOLD_EFFECT_LOADED_DICE] = COMPOUND_STRING("Loaded Dice"),
[HOLD_EFFECT_BOOSTER_ENERGY] = COMPOUND_STRING("Booster Energy"),
[HOLD_EFFECT_OGERPON_MASK] = COMPOUND_STRING("Ogerpon Mask"),
[HOLD_EFFECT_BERSERK_GENE] = COMPOUND_STRING("Berserk Gene"),
};
static const u8 *GetHoldEffectName(enum ItemHoldEffect holdEffect)
{
if (sHoldEffectNames[holdEffect] == NULL)
return sHoldEffectNames[0];
return sHoldEffectNames[holdEffect];
}