Add trainers submenu for debug menu (#8399)

This commit is contained in:
FosterProgramming 2025-12-27 16:57:13 +01:00 committed by GitHub
parent 8de29867d9
commit e95b4de84e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 611 additions and 50 deletions

View File

@ -102,6 +102,7 @@ bool8 ShouldTryRematchBattle(void);
bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId);
bool8 IsTrainerReadyForRematch(void);
void ShouldTryGetTrainerScript(void);
u16 CountMaxPossibleRematch(u16 trainerId);
u16 CountBattledRematchTeams(u16 trainerId);
void TrainerBattleLoadArgs(const u8 *data);
void TrainerBattleLoadArgsTrainerA(const u8 *data);

View File

@ -165,6 +165,7 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId)
void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible);
void FreeAndReserveObjectSpritePalettes(void);
u8 LoadObjectEventPalette(u16 paletteTag);
u8 LoadObjectEventPaletteCopy(u16 originalTag, u16 copyTag);
u8 LoadPlayerObjectEventPalette(u8 gender);
void SetObjectEventSpritePosByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup, s16 x, s16 y);
void ResetObjectSubpriority(u8 localId, u8 mapNum, u8 mapGroup);

View File

@ -32,6 +32,7 @@ void ScriptJump(struct ScriptContext *ctx, const u8 *ptr);
void ScriptCall(struct ScriptContext *ctx, const u8 *ptr);
void ScriptReturn(struct ScriptContext *ctx);
u16 ScriptReadHalfword(struct ScriptContext *ctx);
u16 ScriptPeekHalfword(struct ScriptContext *ctx);
u32 ScriptReadWord(struct ScriptContext *ctx);
u32 ScriptPeekWord(struct ScriptContext *ctx);
void LockPlayerFieldControls(void);
@ -63,6 +64,9 @@ bool32 ValidateSavedRamScript(void);
u8 *GetSavedRamScriptIfValid(void);
void InitRamScript_NoObjectEvent(u8 *script, u16 scriptSize);
bool32 Script_MatchesCallNative(const u8 *script, void *funcPtr, bool32 requestEffects);
bool32 Script_MatchesSpecial(const u8 *script, void *funcPtr);
// srccmd.h
void SetMovingNpcId(u16 npcId);

View File

@ -522,21 +522,6 @@ static void CB2_InitBattleInternal(void)
if (TestRunner_Battle_GetForcedEnvironment())
gBattleEnvironment = TestRunner_Battle_GetForcedEnvironment() - 1;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_TRAINER_HILL
| BATTLE_TYPE_RECORDED)))
{
switch (GetTrainerBattleType(TRAINER_BATTLE_PARAM.opponentA))
{
case TRAINER_BATTLE_TYPE_SINGLES:
break;
case TRAINER_BATTLE_TYPE_DOUBLES:
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
break;
}
}
InitBattleBgsVideo();
LoadBattleTextboxAndBackground();
ResetSpriteData();

View File

@ -1261,6 +1261,10 @@ void BattleSetup_StartTrainerBattle(void)
SetHillTrainerFlag();
}
else if (GetTrainerBattleType(TRAINER_BATTLE_PARAM.opponentA) == TRAINER_BATTLE_TYPE_DOUBLES)
{
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
}
sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers;
gNoOfApproachingTrainers = 0;
@ -1276,13 +1280,28 @@ void BattleSetup_StartTrainerBattle(void)
ScriptContext_Stop();
}
static void CB2_EndDebugBattle(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
for (u32 i = 0; i < 3; i++)
{
u16 monId = gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1;
if (monId < PARTY_SIZE)
SavePlayerPartyMon(gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1, &gPlayerParty[i]);
}
LoadPlayerParty();
}
SetMainCallback2(CB2_EndTrainerBattle);
}
void BattleSetup_StartTrainerBattle_Debug(void)
{
sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers;
gNoOfApproachingTrainers = 0;
sShouldCheckTrainerBScript = FALSE;
gWhichTrainerToFaceAfterBattle = 0;
gMain.savedCallback = CB2_EndTrainerBattle;
gMain.savedCallback = CB2_EndDebugBattle;
CreateBattleStartTask_Debug(GetWildBattleTransition(), 0);
@ -1954,22 +1973,30 @@ void ShouldTryGetTrainerScript(void)
}
}
u16 CountMaxPossibleRematch(u16 trainerId)
{
for (u32 i = 1; i < REMATCHES_COUNT; i++)
{
if (gRematchTable[trainerId].trainerIds[i] == 0)
return i;
}
return REMATCHES_COUNT - 1;
}
u16 CountBattledRematchTeams(u16 trainerId)
{
s32 i;
if (HasTrainerBeenFought(gRematchTable[trainerId].trainerIds[0]) != TRUE)
return 0;
for (i = 1; i < REMATCHES_COUNT; i++)
for (u32 i = 1; i < REMATCHES_COUNT; i++)
{
if (gRematchTable[trainerId].trainerIds[i] == 0)
break;
return i;
if (!HasTrainerBeenFought(gRematchTable[trainerId].trainerIds[i]))
break;
return i;
}
return i;
return REMATCHES_COUNT - 1;
}
void SetMultiTrainerBattle(struct ScriptContext *ctx)

View File

@ -70,6 +70,7 @@
#include "constants/items.h"
#include "constants/map_groups.h"
#include "constants/rgb.h"
#include "constants/script_commands.h"
#include "constants/songs.h"
#include "constants/species.h"
#include "constants/weather.h"
@ -77,6 +78,9 @@
#include "rtc.h"
#include "fake_rtc.h"
#include "save.h"
#include "vs_seeker.h"
#include "load_save.h"
#include "battle_partner.h"
enum FollowerNPCCreateDebugMenu
{
@ -157,6 +161,13 @@ enum DebugBattleEnvironment
DEBUG_BATTLE_2_MENU_ITEM_ENVIRONMENT_9,
};
enum DebugTrainerSelection
{
TRAINERS_DEBUG_SELECTION_TRAINER1,
TRAINERS_DEBUG_SELECTION_TRAINER2,
TRAINERS_DEBUG_SELECTION_PARTNER,
};
// *******************************
// Constants
#define DEBUG_MENU_FONT FONT_NORMAL
@ -181,6 +192,8 @@ enum DebugBattleEnvironment
#define DEBUG_NUMBER_DIGITS_VARIABLE_VALUE 5
#define DEBUG_NUMBER_DIGITS_ITEMS 4
#define DEBUG_NUMBER_DIGITS_ITEM_QUANTITY 3
#define DEBUG_NUMBER_DIGITS_LOCALID 2
#define DEBUG_NUMBER_DIGITS_TRAINERS MAX_DIGITS(TRAINERS_COUNT)
#define DEBUG_NUMBER_ICON_X 210
#define DEBUG_NUMBER_ICON_Y 50
@ -222,6 +235,7 @@ struct DebugMenuListData
struct ListMenuItem listItems[DEBUG_MAX_MENU_ITEMS + 1];
u8 itemNames[DEBUG_MAX_MENU_ITEMS + 1][26];
u8 listId;
s16 data[8];
};
// EWRAM
@ -233,6 +247,7 @@ EWRAM_DATA u64 gDebugAIFlags = 0;
// *******************************
// Define functions
static void Debug_ShowMenu(DebugFunc HandleInput, const struct DebugMenuOption *items);
static u8 Debug_GenerateListTrainerMenu(void);
static u8 Debug_GenerateListMenuNames(void);
static void Debug_DestroyMenu(u8 taskId);
static void DebugAction_Cancel(u8 taskId);
@ -242,6 +257,7 @@ static u8 DebugNativeStep_CreateDebugWindow(void);
static void DebugNativeStep_CloseDebugWindow(u8 taskId);
static void DebugAction_OpenSubMenu(u8 taskId, const struct DebugMenuOption *items);
static void DebugAction_OpenSubMenuTrainers(u8 taskId, const struct DebugMenuOption *items);
static void DebugAction_OpenSubMenuFlagsVars(u8 taskId, const struct DebugMenuOption *items);
static void DebugAction_OpenSubMenuFakeRTC(u8 taskId, const struct DebugMenuOption *items);
static void DebugAction_OpenSubMenuCreateFollowerNPC(u8 taskId, const struct DebugMenuOption *items);
@ -282,6 +298,14 @@ static void DebugAction_Party_ClearParty(u8 taskId);
static void DebugAction_Party_SetParty(u8 taskId);
static void DebugAction_Party_BattleSingle(u8 taskId);
static void DebugAction_Trainers_ChooseFromMap(u8 taskId);
static void DebugAction_Trainers_ChooseTrainer(u8 taskId, u32 selection);
static void DebugAction_Trainers_SwitchDoublesFlag(u8 taskId);
static void DebugAction_Trainers_SetRematch(u8 taskId);
static void DebugAction_Trainers_SetRematchReadiness(u8 taskId);
static void DebugAction_Trainers_TryBattle(u8 taskId);
static void DebugAction_Trainers_RechargeVsSeeker(u8 taskId);
static void DebugAction_FlagsVars_Flags(u8 taskId);
static void DebugAction_FlagsVars_FlagsSelect(u8 taskId);
static void DebugAction_FlagsVars_Vars(u8 taskId);
@ -638,6 +662,20 @@ static const struct DebugMenuOption sDebugMenu_Actions_Scripts[] =
{ NULL }
};
static const struct DebugMenuOption sDebugMenu_Actions_Trainers[] =
{
{ COMPOUND_STRING("Choose trainer from map"), DebugAction_Trainers_ChooseFromMap },
{ COMPOUND_STRING("Trainer 1: {STR_VAR_1}"), DebugAction_Trainers_ChooseTrainer, (void *)TRAINERS_DEBUG_SELECTION_TRAINER1},
{ COMPOUND_STRING("Trainer 2: {STR_VAR_1}"), DebugAction_Trainers_ChooseTrainer, (void *)TRAINERS_DEBUG_SELECTION_TRAINER2},
{ COMPOUND_STRING("Partner: {STR_VAR_1}"), DebugAction_Trainers_ChooseTrainer, (void *)TRAINERS_DEBUG_SELECTION_PARTNER},
{ COMPOUND_STRING("Double Battle: {STR_VAR_1}"), DebugAction_ToggleFlag, DebugAction_Trainers_SwitchDoublesFlag },
{ COMPOUND_STRING("Matches {STR_VAR_1}/{STR_VAR_2}"), DebugAction_ToggleFlag, DebugAction_Trainers_SetRematch },
{ COMPOUND_STRING("Rematch Ready {STR_VAR_1}"), DebugAction_ToggleFlag, DebugAction_Trainers_SetRematchReadiness },
{ COMPOUND_STRING("Try Battle"), DebugAction_Trainers_TryBattle },
{ COMPOUND_STRING("Recharge VS Seeker"), DebugAction_Trainers_RechargeVsSeeker },
{ NULL }
};
static const struct DebugMenuOption sDebugMenu_Actions_Sound[] =
{
{ COMPOUND_STRING("SFX…"), DebugAction_Sound_SE },
@ -691,6 +729,7 @@ static const struct DebugMenuOption sDebugMenu_Actions_Main[] =
{ COMPOUND_STRING("Give X…"), DebugAction_OpenSubMenu, sDebugMenu_Actions_Give, },
{ COMPOUND_STRING("Player…"), DebugAction_OpenSubMenu, sDebugMenu_Actions_Player, },
{ COMPOUND_STRING("Scripts…"), DebugAction_OpenSubMenu, sDebugMenu_Actions_Scripts, },
{ COMPOUND_STRING("Trainers…"), DebugAction_OpenSubMenuTrainers, sDebugMenu_Actions_Trainers, },
{ COMPOUND_STRING("Flags & Vars…"), DebugAction_OpenSubMenuFlagsVars, sDebugMenu_Actions_Flags, },
{ COMPOUND_STRING("Sound…"), DebugAction_OpenSubMenu, sDebugMenu_Actions_Sound, },
{ COMPOUND_STRING("ROM Info…"), DebugAction_OpenSubMenu, sDebugMenu_Actions_ROMInfo2, },
@ -810,7 +849,8 @@ static bool32 IsSubMenuAction(const void *action)
return action == DebugAction_OpenSubMenu
|| action == DebugAction_OpenSubMenuFlagsVars
|| action == DebugAction_OpenSubMenuFakeRTC
|| action == DebugAction_OpenSubMenuCreateFollowerNPC;
|| action == DebugAction_OpenSubMenuCreateFollowerNPC
|| action == DebugAction_OpenSubMenuTrainers;
}
static void Debug_ShowMenu(DebugFunc HandleInput, const struct DebugMenuOption *items)
@ -833,7 +873,11 @@ static void Debug_ShowMenu(DebugFunc HandleInput, const struct DebugMenuOption *
u32 totalItems = 0;
if (sDebugMenuListData->listId == 1)
if (sDebugMenuListData->listId == 2)
{
totalItems = Debug_GenerateListTrainerMenu();
}
else if (sDebugMenuListData->listId == 1)
{
totalItems = Debug_GenerateListMenuNames();
}
@ -983,6 +1027,97 @@ static void DebugNativeStep_CloseDebugWindow(u8 taskId)
UnlockPlayerFieldControls();
}
static u8 Debug_GenerateListTrainerMenu(void)
{
u32 trainer1Id = sDebugMenuListData->data[0];
u32 trainer2Id = sDebugMenuListData->data[2];
u32 partnerId = sDebugMenuListData->data[4];
s32 rematchTableId = sDebugMenuListData->data[1];
bool32 isRealFight = sDebugMenuListData->data[3];
bool32 noDraw;
u32 offset = 0;
u8 totalItems = 0;
for (u32 i = 0; i < (ARRAY_COUNT(sDebugMenu_Actions_Trainers) - 1); i++)
{
noDraw = FALSE;
switch (i)
{
case 1:
ConvertIntToDecimalStringN(gStringVar1, trainer1Id, STR_CONV_MODE_LEADING_ZEROS, 3);
break;
case 2:
if (trainer2Id == TRAINER_NONE)
StringCopy(gStringVar1, COMPOUND_STRING("None"));
else
ConvertIntToDecimalStringN(gStringVar1, trainer2Id, STR_CONV_MODE_LEADING_ZEROS, 3);
break;
case 3:
if (partnerId == PARTNER_NONE)
StringCopy(gStringVar1, COMPOUND_STRING("None"));
else
ConvertIntToDecimalStringN(gStringVar1, partnerId, STR_CONV_MODE_LEADING_ZEROS, 3);
break;
case 4:
if (sDebugMenuListData->data[5] || trainer2Id != TRAINER_NONE || partnerId != PARTNER_NONE)
StringCopy(gStringVar1, COMPOUND_STRING("{COLOR GREEN} TRUE"));
else
StringCopy(gStringVar1, COMPOUND_STRING("{COLOR RED} FALSE"));
break;
case 5:
if (!isRealFight)
{
noDraw = TRUE;
break;
}
if (rematchTableId == -1)
{
if (HasTrainerBeenFought(trainer1Id))
StringCopy(gStringVar1, COMPOUND_STRING("1"));
else
StringCopy(gStringVar1, COMPOUND_STRING("0"));
StringCopy(gStringVar2, COMPOUND_STRING("1"));
}
else
{
ConvertIntToDecimalStringN(gStringVar1, CountBattledRematchTeams(rematchTableId), STR_CONV_MODE_LEADING_ZEROS, 1);
ConvertIntToDecimalStringN(gStringVar2, CountMaxPossibleRematch(rematchTableId), STR_CONV_MODE_LEADING_ZEROS, 1);
}
break;
case 6:
if (I_VS_SEEKER_CHARGING || !isRealFight || rematchTableId == -1)
{
noDraw = TRUE;
break;
}
if (gSaveBlock1Ptr->trainerRematches[rematchTableId])
StringCopy(gStringVar1, COMPOUND_STRING("{COLOR GREEN} TRUE"));
else
StringCopy(gStringVar1, COMPOUND_STRING("{COLOR RED} FALSE"));
break;
case 8:
if (I_VS_SEEKER_CHARGING == 0)
noDraw = TRUE;
break;
}
StringExpandPlaceholders(gStringVar4, sDebugMenu_Actions_Trainers[i].text);
StringCopy(&sDebugMenuListData->itemNames[i][0], gStringVar4);
if (noDraw)
{
offset++;
}
else
{
sDebugMenuListData->listItems[i - offset].name = &sDebugMenuListData->itemNames[i][0];
sDebugMenuListData->listItems[i - offset].id = i;
totalItems++;
}
}
return totalItems;
}
static const u16 sLocationFlags[] =
{
FLAG_VISITED_LITTLEROOT_TOWN,
@ -1179,6 +1314,10 @@ static void DebugTask_HandleMenuInput_General(u8 taskId)
((DebugFunc)option.actionParams)(taskId);
DebugAction_ToggleFlag(taskId);
}
else if (option.action == DebugAction_Trainers_ChooseTrainer)
{
DebugAction_Trainers_ChooseTrainer(taskId, (u32)option.actionParams);
}
else
{
((DebugFunc)option.action)(taskId);
@ -1203,6 +1342,14 @@ static void DebugTask_HandleMenuInput_General(u8 taskId)
}
}
static void DebugAction_OpenSubMenuTrainers(u8 taskId, const struct DebugMenuOption *items)
{
Debug_DestroyMenu(taskId);
sDebugMenuListData->listId = 2;
sDebugMenuListData->data[0] = TRAINER_NONE;
Debug_ShowMenu(DebugTask_HandleMenuInput_General, items);
}
static void DebugAction_OpenSubMenuFlagsVars(u8 taskId, const struct DebugMenuOption *items)
{
Debug_DestroyMenu(taskId);
@ -1237,7 +1384,11 @@ static void DebugAction_ExecuteScript(u8 taskId, const u8 *script)
static void DebugAction_ToggleFlag(u8 taskId)
{
Debug_GenerateListMenuNames();
if (sDebugMenuListData->listId == 2)
Debug_GenerateListTrainerMenu();
else
Debug_GenerateListMenuNames();
RedrawListMenu(gTasks[taskId].tMenuTaskId);
}
@ -1651,6 +1802,372 @@ void DebugMenu_CalculateTimeOfDay(struct ScriptContext *ctx)
}
}
// *******************************
// Actions Trainers
static void Debug_Trainers_ResetTrainersData(void)
{
sDebugMenuListData->data[0] = TRAINER_NONE;
sDebugMenuListData->data[1] = -1;
sDebugMenuListData->data[2] = TRAINER_NONE;
sDebugMenuListData->data[3] = -1;
sDebugMenuListData->data[4] = PARTNER_NONE;
sDebugMenuListData->data[5] = FALSE;
}
void SetMultiTrainerBattle(struct ScriptContext *ctx);
static void ParseObjectEventScript(const u8 *script)
{
struct ScriptContext *ctx = AllocZeroed(sizeof(struct ScriptContext));
if (script[0] == SCR_OP_TRAINERBATTLE)
{
TrainerBattleLoadArgs(script + 1);
}
else if (Script_MatchesCallNative(script, NativeVsSeekerRematchId, TRUE))
{
ctx->scriptPtr = script + 5;
sDebugMenuListData->data[0] = ScriptPeekHalfword(ctx);
}
else if (Script_MatchesSpecial(script, SavePlayerParty) && Script_MatchesCallNative(script + 3, SetMultiTrainerBattle, FALSE))
{
ctx->scriptPtr = script + 8;
SetMultiTrainerBattle(ctx);
}
Free(ctx);
if (!TRAINER_BATTLE_PARAM.opponentA && !TRAINER_BATTLE_PARAM.opponentB)
return;
sDebugMenuListData->data[0] = TRAINER_BATTLE_PARAM.opponentA;
sDebugMenuListData->data[2] = TRAINER_BATTLE_PARAM.opponentB;
if (gPartnerTrainerId)
sDebugMenuListData->data[4] = gPartnerTrainerId - MAX_TRAINERS_COUNT;
InitTrainerBattleParameter();
gPartnerTrainerId = 0;
}
static void Debug_Display_LocalTrainer(u32 localId, u32 digit, u8 windowId)
{
u32 trainerID = sDebugMenuListData->data[0];
StringCopy(gStringVar2, gText_DigitIndicator[digit]);
u8 *end;
if (trainerID == TRAINER_NONE)
end = StringCopy(gStringVar1, COMPOUND_STRING("Not a Trainer"));
else
end = StringCopy(gStringVar1, GetTrainerNameFromId(trainerID));
WrapFontIdToFit(gStringVar1, end, DEBUG_MENU_FONT, WindowWidthPx(windowId));
StringCopyPadded(gStringVar1, gStringVar1, CHAR_SPACE, 15);
ConvertIntToDecimalStringN(gStringVar3, localId, STR_CONV_MODE_LEADING_ZEROS, DEBUG_NUMBER_DIGITS_LOCALID);
StringExpandPlaceholders(gStringVar4, COMPOUND_STRING("Trainer ID: {STR_VAR_3}\n{STR_VAR_1}{CLEAR_TO 90}\n\n{STR_VAR_2}{CLEAR_TO 90}"));
AddTextPrinterParameterized(windowId, DEBUG_MENU_FONT, gStringVar4, 0, 0, 0, NULL);
}
static void GetTrainerIdFromLocalId(u32 localId)
{
Debug_Trainers_ResetTrainersData();
ParseObjectEventScript(gMapHeader.events->objectEvents[localId].script);
if (GetTrainerBattleType(sDebugMenuListData->data[0]) == TRAINER_BATTLE_TYPE_DOUBLES)
sDebugMenuListData->data[5] = TRUE;
}
#define TRAINER_TAG 0xFDF3
#define tSpriteId data[5]
static void DebugAction_ChooseFromMap_Select(u8 taskId)
{
if (JOY_NEW(DPAD_ANY))
{
PlaySE(SE_SELECT);
u32 previousInput = gTasks[taskId].tInput;
do {
Debug_HandleInput_Numeric(taskId, 1, gMapHeader.events->objectEventCount, DEBUG_NUMBER_DIGITS_LOCALID);
GetTrainerIdFromLocalId(gTasks[taskId].tInput);
} while (sDebugMenuListData->data[0] == TRAINER_NONE && gTasks[taskId].tInput != 1 && gTasks[taskId].tInput != gMapHeader.events->objectEventCount);
if (sDebugMenuListData->data[0] == TRAINER_NONE)
{
PlaySE(SE_FAILURE);
gTasks[taskId].tInput = previousInput;
GetTrainerIdFromLocalId(gTasks[taskId].tInput);
return;
}
FreeSpritePaletteByTag(TRAINER_TAG);
DestroySprite(&gSprites[gTasks[taskId].tSpriteId]);
Debug_Display_LocalTrainer(gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
u32 graphicsId = gMapHeader.events->objectEvents[gTasks[taskId].tInput].graphicsId;
gTasks[taskId].tSpriteId = CreateObjectGraphicsSprite(graphicsId, SpriteCallbackDummy, DEBUG_NUMBER_ICON_X, DEBUG_NUMBER_ICON_Y, 4);
StartSpriteAnim(&gSprites[gTasks[taskId].tSpriteId], ANIM_STD_GO_SOUTH);
gSprites[gTasks[taskId].tSpriteId].oam.priority = 0;
gSprites[gTasks[taskId].tSpriteId].oam.paletteNum = LoadObjectEventPaletteCopy(gSprites[gTasks[taskId].tSpriteId].template->paletteTag, TRAINER_TAG);
}
if (JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
{
sDebugMenuListData->data[1] = FirstBattleTrainerIdToRematchTableId(gRematchTable, sDebugMenuListData->data[0]);
sDebugMenuListData->data[3] = TRUE;
FreeSpritePaletteByTag(TRAINER_TAG);
DestroySprite(&gSprites[gTasks[taskId].tSpriteId]);
ClearStdWindowAndFrame(gTasks[taskId].tWindowId, TRUE);
RemoveWindow(gTasks[taskId].tWindowId);
ClearStdWindowAndFrame(gTasks[taskId].tSubWindowId, TRUE);
RemoveWindow(gTasks[taskId].tSubWindowId);
DestroyListMenuTask(gTasks[taskId].tMenuTaskId, NULL, NULL);
DestroyTask(taskId);
PlaySE(SE_SELECT);
sDebugMenuListData->listId = 2;
Debug_RemoveCallbackMenu();
Debug_ShowMenu(DebugTask_HandleMenuInput_General, sDebugMenu_Actions_Trainers);
}
}
static void DebugAction_Trainers_ChooseFromMap(u8 taskId)
{
ClearStdWindowAndFrame(gTasks[taskId].tWindowId, TRUE);
RemoveWindow(gTasks[taskId].tWindowId);
HideMapNamePopUpWindow();
LoadMessageBoxAndBorderGfx();
u32 windowId = AddWindow(&sDebugMenuWindowTemplateExtra);
DrawStdWindowFrame(windowId, FALSE);
CopyWindowToVram(windowId, COPYWIN_FULL);
// Display initial object event
u32 localId = 1;
GetTrainerIdFromLocalId(localId);
Debug_Display_LocalTrainer(localId, 0, windowId);
//Set task data
gTasks[taskId].func = DebugAction_ChooseFromMap_Select;
gTasks[taskId].tSubWindowId = windowId;
gTasks[taskId].tInput = localId;
gTasks[taskId].tDigit = 0;
u32 graphicsId = gMapHeader.events->objectEvents[localId].graphicsId;
u32 spriteId = CreateObjectGraphicsSprite(graphicsId, SpriteCallbackDummy, DEBUG_NUMBER_ICON_X, DEBUG_NUMBER_ICON_Y, 4);
StartSpriteAnim(&gSprites[spriteId], ANIM_STD_GO_SOUTH);
gSprites[spriteId].oam.priority = 0;
gSprites[spriteId].oam.paletteNum = LoadObjectEventPaletteCopy(gSprites[spriteId].template->paletteTag, TRAINER_TAG);
gTasks[taskId].tSpriteId = spriteId;
}
#undef TRAINER_TAG
#undef tSpriteId
#define tSelection data[5]
#define tInitial data[6]
static void Debug_Display_TrainerID(u32 trainerID, u32 selection, u32 digit, u8 windowId)
{
if (selection == TRAINERS_DEBUG_SELECTION_PARTNER)
trainerID = TRAINER_PARTNER(trainerID);
StringCopy(gStringVar2, gText_DigitIndicator[digit]);
u8 *end;
if (trainerID == TRAINER_NONE || trainerID == TRAINER_PARTNER(PARTNER_NONE))
end = StringCopy(gStringVar1, COMPOUND_STRING("None"));
else
end = StringCopy(gStringVar1, GetTrainerNameFromId(trainerID));
WrapFontIdToFit(gStringVar1, end, DEBUG_MENU_FONT, WindowWidthPx(windowId));
StringCopyPadded(gStringVar1, gStringVar1, CHAR_SPACE, 15);
ConvertIntToDecimalStringN(gStringVar3, trainerID, STR_CONV_MODE_LEADING_ZEROS, DEBUG_NUMBER_DIGITS_TRAINERS);
StringExpandPlaceholders(gStringVar4, COMPOUND_STRING("ID: {STR_VAR_3}\n{STR_VAR_1}{CLEAR_TO 90}\n\n{STR_VAR_2}{CLEAR_TO 90}"));
AddTextPrinterParameterized(windowId, DEBUG_MENU_FONT, gStringVar4, 0, 0, 0, NULL);
}
static void DebugAction_ChooseTrainerID_Select(u8 taskId)
{
if (JOY_NEW(DPAD_ANY))
{
PlaySE(SE_SELECT);
u32 min = 1;
u32 max = TRAINERS_COUNT - 1;
if (gTasks[taskId].tSelection == TRAINERS_DEBUG_SELECTION_TRAINER2)
{
min = 0;
max = TRAINERS_COUNT - 1;
}
else if (gTasks[taskId].tSelection == TRAINERS_DEBUG_SELECTION_PARTNER)
{
min = 0;
max = PARTNER_COUNT - 1;
}
Debug_HandleInput_Numeric(taskId, min, max, DEBUG_NUMBER_DIGITS_TRAINERS);
switch(gTasks[taskId].tSelection)
{
case TRAINERS_DEBUG_SELECTION_TRAINER1:
sDebugMenuListData->data[0] = gTasks[taskId].tInput;
break;
case TRAINERS_DEBUG_SELECTION_TRAINER2:
sDebugMenuListData->data[2] = gTasks[taskId].tInput;
break;
case TRAINERS_DEBUG_SELECTION_PARTNER:
sDebugMenuListData->data[4] = gTasks[taskId].tInput;
break;
}
Debug_Display_TrainerID(gTasks[taskId].tInput, gTasks[taskId].tSelection, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
}
if (JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
{
PlaySE(SE_SELECT);
if (gTasks[taskId].tInput != gTasks[taskId].tInitial)
{
sDebugMenuListData->data[3] = FALSE;
sDebugMenuListData->data[1] = -1;
}
ClearStdWindowAndFrame(gTasks[taskId].tWindowId, TRUE);
RemoveWindow(gTasks[taskId].tWindowId);
ClearStdWindowAndFrame(gTasks[taskId].tSubWindowId, TRUE);
RemoveWindow(gTasks[taskId].tSubWindowId);
DestroyListMenuTask(gTasks[taskId].tMenuTaskId, NULL, NULL);
DestroyTask(taskId);
sDebugMenuListData->listId = 2;
Debug_RemoveCallbackMenu();
Debug_ShowMenu(DebugTask_HandleMenuInput_General, sDebugMenu_Actions_Trainers);
}
}
static void DebugAction_Trainers_ChooseTrainer(u8 taskId, u32 selection)
{
ClearStdWindowAndFrame(gTasks[taskId].tWindowId, TRUE);
RemoveWindow(gTasks[taskId].tWindowId);
HideMapNamePopUpWindow();
LoadMessageBoxAndBorderGfx();
u32 windowId = AddWindow(&sDebugMenuWindowTemplateExtra);
DrawStdWindowFrame(windowId, FALSE);
CopyWindowToVram(windowId, COPYWIN_FULL);
//Set task data
gTasks[taskId].func = DebugAction_ChooseTrainerID_Select;
gTasks[taskId].tSubWindowId = windowId;
gTasks[taskId].tDigit = 0;
gTasks[taskId].tSelection = (s32)selection;
switch(gTasks[taskId].tSelection)
{
case TRAINERS_DEBUG_SELECTION_TRAINER1:
gTasks[taskId].tInput = sDebugMenuListData->data[0];
break;
case TRAINERS_DEBUG_SELECTION_TRAINER2:
gTasks[taskId].tInput = sDebugMenuListData->data[2];
break;
case TRAINERS_DEBUG_SELECTION_PARTNER:
gTasks[taskId].tInput = sDebugMenuListData->data[4];
break;
}
gTasks[taskId].tInitial = gTasks[taskId].tInput;
// Display initial trainer
Debug_Display_TrainerID(gTasks[taskId].tInput, gTasks[taskId].tSelection, 0, windowId);
}
#undef tSelection
#undef tInitial
static void DebugAction_Trainers_SwitchDoublesFlag(u8 taskId)
{
if (sDebugMenuListData->data[2] != TRAINER_NONE || sDebugMenuListData->data[4] != PARTNER_NONE)
return;
if (sDebugMenuListData->data[5])
sDebugMenuListData->data[5] = FALSE;
else
sDebugMenuListData->data[5] = TRUE;
}
static void DebugAction_Trainers_SetRematch(u8 taskId)
{
s32 rematchId = sDebugMenuListData->data[1];
if (rematchId == -1)
{
FlagToggle(TRAINER_FLAGS_START + sDebugMenuListData->data[0]);
return;
}
for (u32 i = 0; i < REMATCHES_COUNT; i++)
{
if (gRematchTable[rematchId].trainerIds[i] == 0)
break;
if (!HasTrainerBeenFought(gRematchTable[rematchId].trainerIds[i]))
{
FlagToggle(TRAINER_FLAGS_START + gRematchTable[rematchId].trainerIds[i]);
return;
}
}
for (u32 i = 0; i < REMATCHES_COUNT; i++)
{
if (gRematchTable[rematchId].trainerIds[i] == 0)
break;
FlagToggle(TRAINER_FLAGS_START + gRematchTable[rematchId].trainerIds[i]);
}
}
static void DebugAction_Trainers_SetRematchReadiness(u8 taskId)
{
if (gSaveBlock1Ptr->trainerRematches[sDebugMenuListData->data[1]] == -1)
return;
if (gSaveBlock1Ptr->trainerRematches[sDebugMenuListData->data[1]])
gSaveBlock1Ptr->trainerRematches[sDebugMenuListData->data[1]] = FALSE;
else
gSaveBlock1Ptr->trainerRematches[sDebugMenuListData->data[1]] = TRUE;
}
static void DebugAction_Trainers_TryBattle(u8 taskId)
{
s32 trainer1Id = sDebugMenuListData->data[0];
s32 trainer2Id = sDebugMenuListData->data[2];
s32 partnerId = sDebugMenuListData->data[4];
s32 rematchId = sDebugMenuListData->data[1];
if (sDebugMenuListData->data[1] != -1)
{
s32 lastMatch = CountBattledRematchTeams(rematchId);
if (lastMatch == REMATCHES_COUNT || gRematchTable[rematchId].trainerIds[lastMatch] == 0)
lastMatch -= 1;
trainer1Id = gRematchTable[rematchId].trainerIds[lastMatch];
}
gBattleTypeFlags = BATTLE_TYPE_TRAINER;
TRAINER_BATTLE_PARAM.opponentA = trainer1Id;
TRAINER_BATTLE_PARAM.opponentB = 0xFFFF;
if (sDebugMenuListData->data[5] || partnerId != PARTNER_NONE || trainer2Id != TRAINER_NONE)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
if (trainer2Id != TRAINER_NONE)
{
TRAINER_BATTLE_PARAM.opponentB = trainer2Id;
gBattleTypeFlags |= BATTLE_TYPE_TWO_OPPONENTS;
}
if (partnerId != PARTNER_NONE)
{
SavePlayerParty();
gPartnerTrainerId = TRAINER_PARTNER(partnerId);
gBattleTypeFlags |= BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
for (u32 i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++)
{
gSelectedOrderFromParty[i] = i + 1;
gSaveBlock2Ptr->frontier.selectedPartyMons[i] = gSelectedOrderFromParty[i];
}
FillPartnerParty(gPartnerTrainerId);
}
gBattleEnvironment = BattleSetup_GetEnvironmentId();
CalculateEnemyPartyCount();
BattleSetup_StartTrainerBattle_Debug();
Debug_DestroyMenu_Full(taskId);
}
static void DebugAction_Trainers_RechargeVsSeeker(u8 taskId)
{
gSaveBlock1Ptr->trainerRematchStepCounter = VSSEEKER_RECHARGE_STEPS;
MapResetTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
ScriptContext_SetupScript(EventScript_VsSeekerChargingDone);
Debug_DestroyMenu_Full(taskId);
}
// *******************************
// Actions Flags and Vars
static void Debug_Display_FlagInfo(u32 flag, u32 digit, u8 windowId)

View File

@ -3162,6 +3162,13 @@ u8 LoadObjectEventPalette(u16 paletteTag)
return LoadSpritePaletteIfTagExists(&sObjectEventSpritePalettes[i]);
}
u8 LoadObjectEventPaletteCopy(u16 originalTag, u16 copyTag)
{
u32 i = FindObjectEventPaletteIndexByTag(originalTag);
const struct SpritePalette palette = {sObjectEventSpritePalettes[i].data, copyTag};
return LoadSpritePalette(&palette);
}
u8 LoadPlayerObjectEventPalette(u8 gender)
{
u16 paletteTag;

View File

@ -8,6 +8,7 @@
#include "constants/event_objects.h"
#include "constants/flags.h"
#include "constants/map_scripts.h"
#include "constants/script_commands.h"
#include "field_message_box.h"
#define RAM_SCRIPT_MAGIC 51
@ -182,6 +183,13 @@ u16 ScriptReadHalfword(struct ScriptContext *ctx)
return value;
}
u16 ScriptPeekHalfword(struct ScriptContext *ctx)
{
u16 value = *(ctx->scriptPtr);
value |= *(ctx->scriptPtr + 1) << 8;
return value;
}
u32 ScriptReadWord(struct ScriptContext *ctx)
{
u32 value0 = *(ctx->scriptPtr++);
@ -641,3 +649,28 @@ void Script_RequestWriteVar_Internal(u32 varId)
return;
Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE);
}
bool32 Script_MatchesCallNative(const u8 *script, void *funcPtr, bool32 requestEffects)
{
if (script[0] != SCR_OP_CALLNATIVE)
return FALSE;
u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1];
u32 targetFunc = (u32)funcPtr;
if (requestEffects)
targetFunc |= 0xA000000;
if (callnativeFunc == targetFunc)
return TRUE;
return FALSE;
}
bool32 Script_MatchesSpecial(const u8 *script, void *funcPtr)
{
if (script[0] != SCR_OP_SPECIAL)
return FALSE;
typedef u16 (*SpecialFunc)(void);
extern const SpecialFunc gSpecials[];
SpecialFunc specialFunc = gSpecials[(script[2] << 8) + script[1]];
if ((u32)specialFunc == ((u32)funcPtr))
return TRUE;
return FALSE;
}

View File

@ -717,33 +717,19 @@ static u16 GetTrainerFlagFromScript(const u8 *script)
{
// The trainer flag is located 3 bytes (command + flags + localIdA) from the script pointer, assuming the trainerbattle command is first in the script.
// Because scripts are unaligned, and because the ARM processor requires shorts to be 16-bit aligned, this function needs to perform explicit bitwise operations to get the correct flag.
u16 trainerFlag;
switch (script[0])
u16 trainerFlag = TRAINER_NONE;
struct ScriptContext *ctx = AllocZeroed(sizeof(struct ScriptContext));
if (script[0] == SCR_OP_TRAINERBATTLE)
{
case SCR_OP_TRAINERBATTLE:
script += 3;
trainerFlag = script[0];
trainerFlag |= script[1] << 8;
break;
case SCR_OP_CALLNATIVE:
{
u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1];
if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function
{
script += 5;
trainerFlag = script[0];
trainerFlag |= script[1] << 8;
}
else
{
trainerFlag = TRAINER_NONE;
}
break;
}
default:
trainerFlag = TRAINER_NONE;
break;
ctx->scriptPtr = script + 3;
trainerFlag = ScriptPeekHalfword(ctx);
}
else if (Script_MatchesCallNative(script, NativeVsSeekerRematchId, TRUE))
{
ctx->scriptPtr = script + 5;
trainerFlag = ScriptPeekHalfword(ctx);
}
Free(ctx);
return trainerFlag;
}