pokeemmo/src/battle_debug.c
2025-12-18 12:04:25 +08:00

2300 lines
83 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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"
#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 HoldEffect holdEffect);
// const rom data
static const u8 sText_Ability[] = _("特性");
static const u8 sText_HeldItem[] = _("持有物");
static const u8 sText_HoldEffect[] = _("携带效果");
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("招式"), LIST_ITEM_MOVES},
{sText_Ability, LIST_ITEM_ABILITY},
{sText_HeldItem, LIST_ITEM_HELD_ITEM},
{COMPOUND_STRING("PP"), LIST_ITEM_PP},
{COMPOUND_STRING("属性"), LIST_ITEM_TYPES},
{COMPOUND_STRING("能力"), LIST_ITEM_STATS},
{COMPOUND_STRING("能力阶级"), LIST_ITEM_STAT_STAGES},
{COMPOUND_STRING("状态1"), LIST_ITEM_STATUS1},
{COMPOUND_STRING("临时状态"), LIST_ITEM_VOLATILE},
{COMPOUND_STRING("场地陷阱"), LIST_ITEM_HAZARDS},
{COMPOUND_STRING("场地状态"), LIST_ITEM_SIDE_STATUS},
{COMPOUND_STRING("AI"), LIST_ITEM_AI},
{COMPOUND_STRING("AI点数/伤害"), LIST_ITEM_AI_MOVES_PTS},
{COMPOUND_STRING("AI信息"), LIST_ITEM_AI_INFO},
{COMPOUND_STRING("AI队伍"), LIST_ITEM_AI_PARTY},
{COMPOUND_STRING("杂项"), LIST_ITEM_VARIOUS},
{COMPOUND_STRING("立即胜利"), LIST_ITEM_INSTANT_WIN},
};
static const struct ListMenuItem sStatsListItems[] =
{
{COMPOUND_STRING("当前HP"), LIST_STAT_HP_CURRENT},
{COMPOUND_STRING("最大HP"), LIST_STAT_HP_MAX},
{COMPOUND_STRING("攻击"), LIST_STAT_ATTACK},
{COMPOUND_STRING("防御"), LIST_STAT_DEFENSE},
{COMPOUND_STRING("速度"), LIST_STAT_SPEED},
{COMPOUND_STRING("特攻"), LIST_STAT_SP_ATK},
{COMPOUND_STRING("特防"), LIST_STAT_SP_DEF},
};
static const struct ListMenuItem sStatus1ListItems[] =
{
{COMPOUND_STRING("睡眠"), LIST_STATUS1_SLEEP},
{COMPOUND_STRING("中毒"), LIST_STATUS1_POISON},
{COMPOUND_STRING("灼伤"), LIST_STATUS1_BURN},
{COMPOUND_STRING("冰冻"), LIST_STATUS1_FREEZE},
{COMPOUND_STRING("麻痹"), LIST_STATUS1_PARALYSIS},
{COMPOUND_STRING("剧毒"), LIST_STATUS1_TOXIC_POISON},
{COMPOUND_STRING("剧毒经过回合"), LIST_STATUS1_TOXIC_COUNTER},
{COMPOUND_STRING("冻伤"), LIST_STATUS1_FROSTBITE},
};
static const struct ListMenuItem sVolatileStatusListItems[] =
{
{COMPOUND_STRING("念力"), VOLATILE_CONFUSION},
{COMPOUND_STRING("畏缩"), VOLATILE_FLINCHED},
{COMPOUND_STRING("无理取闹"), VOLATILE_TORMENT},
{COMPOUND_STRING("粉尘"), VOLATILE_POWDER},
{COMPOUND_STRING("变圆"), VOLATILE_DEFENSE_CURL},
{COMPOUND_STRING("愤怒"), VOLATILE_RAGE},
{COMPOUND_STRING("同命"), VOLATILE_DESTINY_BOND},
{COMPOUND_STRING("无法逃脱"), VOLATILE_ESCAPE_PREVENTION},
{COMPOUND_STRING("诅咒"), VOLATILE_CURSED},
{COMPOUND_STRING("识破"), VOLATILE_FORESIGHT},
{COMPOUND_STRING("龙声鼓舞"), VOLATILE_DRAGON_CHEER},
{COMPOUND_STRING("聚气"), VOLATILE_FOCUS_ENERGY},
{COMPOUND_STRING("输电"), VOLATILE_ELECTRIFIED},
{COMPOUND_STRING("玩泥巴"), VOLATILE_MUD_SPORT},
{COMPOUND_STRING("玩水"), VOLATILE_WATER_SPORT},
{COMPOUND_STRING("无限混乱"), VOLATILE_INFINITE_CONFUSION},
{COMPOUND_STRING("盐腌"), VOLATILE_SALT_CURE},
{COMPOUND_STRING("满身糖"), VOLATILE_SYRUP_BOMB},
{COMPOUND_STRING("巨剑突击"), VOLATILE_GLAIVE_RUSH},
{COMPOUND_STRING("寄生种子"), VOLATILE_LEECH_SEED},
{COMPOUND_STRING("锁定"), VOLATILE_LOCK_ON},
{COMPOUND_STRING("终焉之歌"), VOLATILE_PERISH_SONG},
{COMPOUND_STRING("变小"), VOLATILE_MINIMIZE},
{COMPOUND_STRING("充电"), VOLATILE_CHARGE_TIMER},
{COMPOUND_STRING("扎根"), VOLATILE_ROOT},
{COMPOUND_STRING("哈欠"), VOLATILE_YAWN},
{COMPOUND_STRING("封印"), VOLATILE_IMPRISON},
{COMPOUND_STRING("怨念"), VOLATILE_GRUDGE},
{COMPOUND_STRING("胃液"), VOLATILE_GASTRO_ACID},
{COMPOUND_STRING("查封"), VOLATILE_EMBARGO},
{COMPOUND_STRING("击落"), VOLATILE_SMACK_DOWN},
{COMPOUND_STRING("意念移物"), VOLATILE_TELEKINESIS},
{COMPOUND_STRING("奇迹之眼"), VOLATILE_MIRACLE_EYE},
{COMPOUND_STRING("电磁飘浮"), VOLATILE_MAGNET_RISE},
{COMPOUND_STRING("回复封锁"), VOLATILE_HEAL_BLOCK},
{COMPOUND_STRING("水流环"), VOLATILE_AQUA_RING},
{COMPOUND_STRING("磨砺"), VOLATILE_LASER_FOCUS},
{COMPOUND_STRING("力量戏法"), VOLATILE_POWER_TRICK},
};
static const struct ListMenuItem sHazardsListItems[] =
{
{COMPOUND_STRING("撒菱"), LIST_SIDE_SPIKES},
{COMPOUND_STRING("黏黏网"), LIST_SIDE_STICKY_WEB},
{COMPOUND_STRING("毒菱"), LIST_SIDE_TOXIC_SPIKES},
{COMPOUND_STRING("隐形岩"), LIST_SIDE_STEALTH_ROCK},
{COMPOUND_STRING("超极巨钢铁阵法"), LIST_SIDE_STEELSURGE},
};
static const struct ListMenuItem sSideStatusListItems[] =
{
{COMPOUND_STRING("反射壁"), LIST_SIDE_REFLECT},
{COMPOUND_STRING("光墙"), LIST_SIDE_LIGHTSCREEN},
{COMPOUND_STRING("神秘守护"), LIST_SIDE_SAFEGUARD},
{COMPOUND_STRING("白雾"), LIST_SIDE_MIST},
{COMPOUND_STRING("顺风"), LIST_SIDE_TAILWIND},
{COMPOUND_STRING("极光幕"), LIST_SIDE_AURORA_VEIL},
{COMPOUND_STRING("幸运咒语"), LIST_SIDE_LUCKY_CHANT},
{COMPOUND_STRING("无属性攻击?"), LIST_SIDE_DAMAGE_NON_TYPES},
{COMPOUND_STRING("彩虹"), LIST_SIDE_RAINBOW},
{COMPOUND_STRING("火海"), LIST_SIDE_SEA_OF_FIRE},
{COMPOUND_STRING("湿地"), LIST_SIDE_SWAMP},
};
static const struct ListMenuItem sAIListItems[] =
{
{COMPOUND_STRING("检查效果不好的招式"), LIST_AI_CHECK_BAD_MOVE},
{COMPOUND_STRING("尝试击晕"), LIST_AI_TRY_TO_FAINT},
{COMPOUND_STRING("检查可行性"), LIST_AI_CHECK_VIABILITY},
{COMPOUND_STRING("遵循第一回合设置"), LIST_AI_FORCE_SETUP_FIRST_TURN},
{COMPOUND_STRING("危险大胆"), LIST_AI_RISKY},
{COMPOUND_STRING("尝试两招击倒"), LIST_AI_TRY_TO_2HKO},
{COMPOUND_STRING("偏好接力棒"), LIST_AI_PREFER_BATON_PASS},
{COMPOUND_STRING("双打对战"), LIST_AI_DOUBLE_BATTLE},
{COMPOUND_STRING("注意HP"), LIST_AI_HP_AWARE},
{COMPOUND_STRING("异常状态干扰?"), LIST_AI_POWERFUL_STATUS},
{COMPOUND_STRING("纯朴无效?"), LIST_AI_NEGATE_UNAWARE},
{COMPOUND_STRING("愿意自杀"), LIST_AI_WILL_SUICIDE},
{COMPOUND_STRING("偏好状态变化招式"), LIST_AI_PREFER_STATUS_MOVES},
{COMPOUND_STRING("拖延消耗"), LIST_AI_STALL},
{COMPOUND_STRING("聪明替换"), LIST_AI_SMART_SWITCHING},
{COMPOUND_STRING("主力宝可梦"), LIST_AI_ACE_POKEMON},
{COMPOUND_STRING("全知"), LIST_AI_OMNISCIENT},
{COMPOUND_STRING("聪明的宝可梦选择"), LIST_AI_SMART_MON_CHOICES},
{COMPOUND_STRING("保守"), LIST_AI_CONSERVATIVE},
{COMPOUND_STRING("按顺序替换"), LIST_AI_SEQUENCE_SWITCHING},
{COMPOUND_STRING("双主力宝可梦"), LIST_AI_DOUBLE_ACE_POKEMON},
{COMPOUND_STRING("权衡特性预测"), LIST_AI_WEIGH_ABILITY_PREDICTION},
{COMPOUND_STRING("偏好最高伤害招式"), LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE},
{COMPOUND_STRING("预测替换"), LIST_AI_PREDICT_SWITCH},
{COMPOUND_STRING("预测上场宝可梦"), LIST_AI_PREDICT_INCOMING_MON},
{COMPOUND_STRING("动态功能"), LIST_AI_DYNAMIC_FUNC},
{COMPOUND_STRING("四处游走"), LIST_AI_ROAMING},
{COMPOUND_STRING("狩猎地带"), LIST_AI_SAFARI},
{COMPOUND_STRING("第一战"), LIST_AI_FIRST_BATTLE},
};
static const struct ListMenuItem sVariousListItems[] =
{
{COMPOUND_STRING("显示HP"), VARIOUS_SHOW_HP},
{COMPOUND_STRING("替换HP"), VARIOUS_SUBSTITUTE_HP},
{COMPOUND_STRING("着迷"), 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;
}
}
enum {
COLORID_RED,
};
static const u8 sTextColorTable[][3] =
{
[COLORID_RED] = {TEXT_COLOR_WHITE, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED},
};
static void PutMovesPointsText(struct BattleDebugMenu *data)
{
u32 i, j, count, battlerDef, chosenMoveIndex = gAiBattleData->chosenMoveIndex[data->aiBattlerId];
u8 *text = Alloc(0x50);
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, COMPOUND_STRING("Score/Dmg"), 3, 0, 0, NULL);
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) + 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);
// If chosen move and chosen target
if ((chosenMoveIndex == i) && (gAiBattleData->chosenTarget[data->aiBattlerId] == j) && !(gAiLogicData->shouldSwitch & (1u << data->aiBattlerId)))
AddTextPrinterParameterized3(data->aiMovesWindowId, FONT_NORMAL, 84 + count * 54, (i * 15) + 15, sTextColorTable[COLORID_RED], 0, text);
else
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 84 + count * 54, (i * 15) + 15, 0, NULL);
if ((chosenMoveIndex == i) && (gAiBattleData->chosenTarget[data->aiBattlerId] == j) && !(gAiLogicData->shouldSwitch & (1u << data->aiBattlerId)))
AddTextPrinterParameterized3(data->aiMovesWindowId, FONT_NORMAL, 103 + count * 54, (i * 15) + 15, sTextColorTable[COLORID_RED], 0, COMPOUND_STRING("/"));
else
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, COMPOUND_STRING("/"), 103 + count * 54, (i * 15) + 15, 0, NULL);
ConvertIntToDecimalStringN(text,
AI_GetDamage(data->aiBattlerId, battlerDef, i, AI_ATTACKING, gAiLogicData),
STR_CONV_MODE_LEADING_ZEROS, 3);
if ((chosenMoveIndex == i) && (gAiBattleData->chosenTarget[data->aiBattlerId] == j) && !(gAiLogicData->shouldSwitch & (1u << data->aiBattlerId)))
AddTextPrinterParameterized3(data->aiMovesWindowId, FONT_NORMAL, 110 + count * 54, (i * 15) + 15, sTextColorTable[COLORID_RED], 0, text);
else
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 110 + count * 54, (i * 15) + 15, 0, NULL);
count++;
}
}
if (gAiLogicData->shouldSwitch & (1u << data->aiBattlerId))
{
struct Pokemon *party = GetBattlerParty(data->aiBattlerId);
u32 switchMon = GetMonData(&party[gAiLogicData->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES);
AddTextPrinterParameterized3(data->aiMovesWindowId, FONT_NORMAL, 74, 79, sTextColorTable[COLORID_RED], 0, COMPOUND_STRING("替换为"));
AddTextPrinterParameterized3(data->aiMovesWindowId, FONT_NORMAL, 74 + 68, 79, sTextColorTable[COLORID_RED], 0, gSpeciesInfo[switchMon].speciesName);
}
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,
106 + (count * 54), 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, 135, 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))
{
enum Ability ability = gAiLogicData->abilities[i];
enum HoldEffect 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[] = _("全部");
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++)
{
enum Type *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("回复HP"),
[HOLD_EFFECT_CURE_PAR] = COMPOUND_STRING("麻痹解除"),
[HOLD_EFFECT_CURE_SLP] = COMPOUND_STRING("睡眠解除"),
[HOLD_EFFECT_CURE_PSN] = COMPOUND_STRING("中毒解除"),
[HOLD_EFFECT_CURE_BRN] = COMPOUND_STRING("灼伤解除"),
[HOLD_EFFECT_CURE_FRZ] = COMPOUND_STRING("冰冻解除"),
[HOLD_EFFECT_RESTORE_PP] = COMPOUND_STRING("回复PP"),
[HOLD_EFFECT_CURE_CONFUSION] = COMPOUND_STRING("混乱解除"),
[HOLD_EFFECT_CURE_STATUS] = COMPOUND_STRING("异常状态解除"),
[HOLD_EFFECT_CONFUSE_SPICY] = COMPOUND_STRING("因树果太辣混乱"),
[HOLD_EFFECT_CONFUSE_DRY] = COMPOUND_STRING("因树果太涩混乱"),
[HOLD_EFFECT_CONFUSE_SWEET] = COMPOUND_STRING("因树果太甜混乱"),
[HOLD_EFFECT_CONFUSE_BITTER] = COMPOUND_STRING("因树果太苦混乱"),
[HOLD_EFFECT_CONFUSE_SOUR] = COMPOUND_STRING("因树果太酸混乱"),
[HOLD_EFFECT_ATTACK_UP] = COMPOUND_STRING("攻击提升"),
[HOLD_EFFECT_DEFENSE_UP] = COMPOUND_STRING("防御提升"),
[HOLD_EFFECT_SPEED_UP] = COMPOUND_STRING("速度提升"),
[HOLD_EFFECT_SP_ATTACK_UP] = COMPOUND_STRING("特攻提升"),
[HOLD_EFFECT_SP_DEFENSE_UP] = COMPOUND_STRING("特防提升"),
[HOLD_EFFECT_CRITICAL_UP] = COMPOUND_STRING("击中要害率提升"),
[HOLD_EFFECT_RANDOM_STAT_UP] = COMPOUND_STRING("随机能力提升"),
[HOLD_EFFECT_EVASION_UP] = COMPOUND_STRING("闪避率提升"),
[HOLD_EFFECT_WHITE_HERB] = COMPOUND_STRING("能力恢复"),
[HOLD_EFFECT_MACHO_BRACE] = COMPOUND_STRING("强制锻炼器"),
[HOLD_EFFECT_EXP_SHARE] = COMPOUND_STRING("学习装置"),
[HOLD_EFFECT_QUICK_CLAW] = COMPOUND_STRING("先制之爪"),
[HOLD_EFFECT_FRIENDSHIP_UP] = COMPOUND_STRING("亲密度上升"),
[HOLD_EFFECT_MENTAL_HERB] = COMPOUND_STRING("心灵香草"),
[HOLD_EFFECT_CHOICE_BAND] = COMPOUND_STRING("讲究头带"),
[HOLD_EFFECT_FLINCH] = COMPOUND_STRING("畏缩"),
[HOLD_EFFECT_DOUBLE_PRIZE] = COMPOUND_STRING("零花钱翻倍"),
[HOLD_EFFECT_REPEL] = COMPOUND_STRING("除虫喷雾"),
[HOLD_EFFECT_SOUL_DEW] = COMPOUND_STRING("心之水滴"),
[HOLD_EFFECT_DEEP_SEA_TOOTH] = COMPOUND_STRING("深海之牙"),
[HOLD_EFFECT_DEEP_SEA_SCALE] = COMPOUND_STRING("深海鳞片"),
[HOLD_EFFECT_CAN_ALWAYS_RUN] = COMPOUND_STRING("必定能逃走"),
[HOLD_EFFECT_PREVENT_EVOLVE] = COMPOUND_STRING("不会进化"),
[HOLD_EFFECT_FOCUS_BAND] = COMPOUND_STRING("气势头带"),
[HOLD_EFFECT_LUCKY_EGG] = COMPOUND_STRING("幸运蛋"),
[HOLD_EFFECT_SCOPE_LENS] = COMPOUND_STRING("焦点镜"),
[HOLD_EFFECT_LEFTOVERS] = COMPOUND_STRING("吃剩的东西"),
[HOLD_EFFECT_DRAGON_SCALE] = COMPOUND_STRING("龙之鳞片"),
[HOLD_EFFECT_LIGHT_BALL] = COMPOUND_STRING("电气球"),
[HOLD_EFFECT_TYPE_POWER] = COMPOUND_STRING("属性道具"),
[HOLD_EFFECT_UPGRADE] = COMPOUND_STRING("升级数据"),
[HOLD_EFFECT_SHELL_BELL] = COMPOUND_STRING("贝壳之铃"),
[HOLD_EFFECT_LUCKY_PUNCH] = COMPOUND_STRING("吉利拳"),
[HOLD_EFFECT_METAL_POWDER] = COMPOUND_STRING("金属粉"),
[HOLD_EFFECT_THICK_CLUB] = COMPOUND_STRING("粗骨头"),
[HOLD_EFFECT_LEEK] = COMPOUND_STRING("大葱"),
[HOLD_EFFECT_CHOICE_SCARF] = COMPOUND_STRING("讲究围巾"),
[HOLD_EFFECT_CHOICE_SPECS] = COMPOUND_STRING("讲究眼镜"),
[HOLD_EFFECT_DAMP_ROCK] = COMPOUND_STRING("潮湿岩石"),
[HOLD_EFFECT_GRIP_CLAW] = COMPOUND_STRING("紧缠钩爪"),
[HOLD_EFFECT_HEAT_ROCK] = COMPOUND_STRING("炽热岩石"),
[HOLD_EFFECT_ICY_ROCK] = COMPOUND_STRING("冰冷岩石"),
[HOLD_EFFECT_LIGHT_CLAY] = COMPOUND_STRING("光之黏土"),
[HOLD_EFFECT_SMOOTH_ROCK] = COMPOUND_STRING("沙沙岩石"),
[HOLD_EFFECT_POWER_HERB] = COMPOUND_STRING("强力香草"),
[HOLD_EFFECT_BIG_ROOT] = COMPOUND_STRING("大根茎"),
[HOLD_EFFECT_EXPERT_BELT] = COMPOUND_STRING("达人带"),
[HOLD_EFFECT_LIFE_ORB] = COMPOUND_STRING("生命宝珠"),
[HOLD_EFFECT_METRONOME] = COMPOUND_STRING("挥指"),
[HOLD_EFFECT_MUSCLE_BAND] = COMPOUND_STRING("力量头带"),
[HOLD_EFFECT_WIDE_LENS] = COMPOUND_STRING("广角镜"),
[HOLD_EFFECT_WISE_GLASSES] = COMPOUND_STRING("博识眼镜"),
[HOLD_EFFECT_ZOOM_LENS] = COMPOUND_STRING("对焦镜"),
[HOLD_EFFECT_LAGGING_TAIL] = COMPOUND_STRING("后攻之尾"),
[HOLD_EFFECT_FOCUS_SASH] = COMPOUND_STRING("气势披带"),
[HOLD_EFFECT_FLAME_ORB] = COMPOUND_STRING("火焰宝珠"),
[HOLD_EFFECT_TOXIC_ORB] = COMPOUND_STRING("剧毒宝珠"),
[HOLD_EFFECT_STICKY_BARB] = COMPOUND_STRING("附着针"),
[HOLD_EFFECT_IRON_BALL] = COMPOUND_STRING("黑色铁球"),
[HOLD_EFFECT_BLACK_SLUDGE] = COMPOUND_STRING("黑色污泥"),
[HOLD_EFFECT_DESTINY_KNOT] = COMPOUND_STRING("红线"),
[HOLD_EFFECT_SHED_SHELL] = COMPOUND_STRING("美丽空壳"),
[HOLD_EFFECT_QUICK_POWDER] = COMPOUND_STRING("速度粉"),
[HOLD_EFFECT_ADAMANT_ORB] = COMPOUND_STRING("金刚宝珠"),
[HOLD_EFFECT_LUSTROUS_ORB] = COMPOUND_STRING("白玉宝珠"),
[HOLD_EFFECT_GRISEOUS_ORB] = COMPOUND_STRING("白金宝珠"),
[HOLD_EFFECT_ENIGMA_BERRY] = COMPOUND_STRING("谜芝果"),
[HOLD_EFFECT_RESIST_BERRY] = COMPOUND_STRING("属性抵抗类树果"),
[HOLD_EFFECT_POWER_ITEM] = COMPOUND_STRING("属性增强类道具"),
[HOLD_EFFECT_RESTORE_PCT_HP] = COMPOUND_STRING("一定比例HP回复"),
[HOLD_EFFECT_MICLE_BERRY] = COMPOUND_STRING("奇秘果"),
[HOLD_EFFECT_CUSTAP_BERRY] = COMPOUND_STRING("释陀果"),
[HOLD_EFFECT_JABOCA_BERRY] = COMPOUND_STRING("嘉珍果"),
[HOLD_EFFECT_ROWAP_BERRY] = COMPOUND_STRING("雾莲果"),
[HOLD_EFFECT_KEE_BERRY] = COMPOUND_STRING("亚开果"),
[HOLD_EFFECT_MARANGA_BERRY] = COMPOUND_STRING("香罗果"),
[HOLD_EFFECT_PLATE] = COMPOUND_STRING("属性石板"),
[HOLD_EFFECT_FLOAT_STONE] = COMPOUND_STRING("轻石"),
[HOLD_EFFECT_EVIOLITE] = COMPOUND_STRING("进化奇石"),
[HOLD_EFFECT_ASSAULT_VEST] = COMPOUND_STRING("突击背心"),
[HOLD_EFFECT_DRIVE] = COMPOUND_STRING("属性卡带"),
[HOLD_EFFECT_GEMS] = COMPOUND_STRING("属性宝石"),
[HOLD_EFFECT_ROCKY_HELMET] = COMPOUND_STRING("凸凸头盔"),
[HOLD_EFFECT_AIR_BALLOON] = COMPOUND_STRING("气球"),
[HOLD_EFFECT_RED_CARD] = COMPOUND_STRING("红牌"),
[HOLD_EFFECT_RING_TARGET] = COMPOUND_STRING("标靶"),
[HOLD_EFFECT_BINDING_BAND] = COMPOUND_STRING("紧绑束带"),
[HOLD_EFFECT_EJECT_BUTTON] = COMPOUND_STRING("逃脱按键"),
[HOLD_EFFECT_ABSORB_BULB] = COMPOUND_STRING("球根"),
[HOLD_EFFECT_CELL_BATTERY] = COMPOUND_STRING("充电电池"),
[HOLD_EFFECT_MEGA_STONE] = COMPOUND_STRING("超级进化石"),
[HOLD_EFFECT_SAFETY_GOGGLES] = COMPOUND_STRING("防尘护目镜"),
[HOLD_EFFECT_LUMINOUS_MOSS] = COMPOUND_STRING("光苔"),
[HOLD_EFFECT_SNOWBALL] = COMPOUND_STRING("雪丸"),
[HOLD_EFFECT_WEAKNESS_POLICY] = COMPOUND_STRING("弱点保险"),
[HOLD_EFFECT_PRIMAL_ORB] = COMPOUND_STRING("原始回归宝珠"),
[HOLD_EFFECT_PROTECTIVE_PADS] = COMPOUND_STRING("部位护具"),
[HOLD_EFFECT_TERRAIN_EXTENDER] = COMPOUND_STRING("大地膜"),
[HOLD_EFFECT_TERRAIN_SEED] = COMPOUND_STRING("场地种子"),
[HOLD_EFFECT_ADRENALINE_ORB] = COMPOUND_STRING("胆怯球"),
[HOLD_EFFECT_MEMORY] = COMPOUND_STRING("属性存储碟"),
[HOLD_EFFECT_Z_CRYSTAL] = COMPOUND_STRING("Z纯晶"),
[HOLD_EFFECT_UTILITY_UMBRELLA] = COMPOUND_STRING("万能伞"),
[HOLD_EFFECT_EJECT_PACK] = COMPOUND_STRING("避难背包"),
[HOLD_EFFECT_ROOM_SERVICE] = COMPOUND_STRING("客房服务"),
[HOLD_EFFECT_BLUNDER_POLICY] = COMPOUND_STRING("打空保险"),
[HOLD_EFFECT_HEAVY_DUTY_BOOTS] = COMPOUND_STRING("厚底靴"),
[HOLD_EFFECT_THROAT_SPRAY] = COMPOUND_STRING("爽喉喷雾"),
[HOLD_EFFECT_ABILITY_SHIELD] = COMPOUND_STRING("特性护具"),
[HOLD_EFFECT_CLEAR_AMULET] = COMPOUND_STRING("清净坠饰"),
[HOLD_EFFECT_MIRROR_HERB] = COMPOUND_STRING("模仿香草"),
[HOLD_EFFECT_PUNCHING_GLOVE] = COMPOUND_STRING("拳击手套"),
[HOLD_EFFECT_COVERT_CLOAK] = COMPOUND_STRING("密探斗篷"),
[HOLD_EFFECT_LOADED_DICE] = COMPOUND_STRING("机变骰子"),
[HOLD_EFFECT_BOOSTER_ENERGY] = COMPOUND_STRING("驱劲能量"),
[HOLD_EFFECT_OGERPON_MASK] = COMPOUND_STRING("厄诡椪面具"),
[HOLD_EFFECT_BERSERK_GENE] = COMPOUND_STRING("破坏基因"),
};
static const u8 *GetHoldEffectName(enum HoldEffect holdEffect)
{
if (sHoldEffectNames[holdEffect] == NULL)
return sHoldEffectNames[0];
return sHoldEffectNames[holdEffect];
}