[script-command, dynmultichoice] implement event handler

This commit is contained in:
sbird 2023-01-17 21:21:07 +01:00
parent 276ce62d95
commit a7cd4ca592
5 changed files with 161 additions and 14 deletions

View file

@ -1729,7 +1729,7 @@
.2byte \quantity
.endm
.macro _dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req argv:vararg
.macro _dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req, callbacks:req argv:vararg
.byte 0xe3
.byte \left
.byte \top
@ -1737,6 +1737,7 @@
.byte \maxBeforeScroll
.byte \shouldSort
.2byte \initialSelected
.byte \callbacks
.byte (.Ldynmultichoice_\@_2 - .Ldynmultichoice_\@_1) / 4
.Ldynmultichoice_\@_1:
.4byte \argv
@ -1746,18 +1747,18 @@
@ Displays a multichoice box from which the user can choose a selection, and blocks script execution until a selection is made.
@ Lists of options are provided in argv.
@ If ignoreBPress is set to a non-zero value, then the user will not be allowed to back out of the multichoice with the B button.
.macro dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req argv:vararg
_dynamicmultichoice \left, \top, \ignoreBPress, \maxBeforeScroll, FALSE, \initialSelected, \argv
.macro dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, initialSelected:req, callbacks:req argv:vararg
_dynmultichoice \left, \top, \ignoreBPress, \maxBeforeScroll, FALSE, \initialSelected, \callbacks, \argv
.endm
.macro dynmultipush name:req, id:req
.byte 0xe4
.4byte \name
.byte \id
.2byte \id
.endm
.macro dynmultistack left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req
_dynmultichoice \left, \top, \ignoreBPress, \maxBeforeScroll, \shouldSort, \initialSelected, NULL
.macro dynmultistack left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req, callbacks:req
_dynmultichoice \left, \top, \ignoreBPress, \maxBeforeScroll, \shouldSort, \initialSelected, \callbacks, NULL
.endm

View file

@ -165,4 +165,10 @@
#define STDSTRING_BATTLE_PIKE 28
#define STDSTRING_BATTLE_PYRAMID 29
// Dynamic Multichoice Callbacks
#define DYN_MULTICHOICE_CB_DEBUG 0
#define DYN_MULTICHOICE_CB_SHOW_ITEM 1
#define DYN_MULTICHOICE_CB_NONE 255
#endif //GUARD_SCRIPT_MENU_CONSTANTS_H

View file

@ -29,7 +29,7 @@ struct ListMenuItem *MultichoiceDynamic_PopElement(void);
struct ListMenuItem *MultichoiceDynamic_PeekElement(void);
struct ListMenuItem *MultichoiceDynamic_PeekElementAt(u32 index);
void MultichoiceDynamic_DestroyStack(void);
bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u8 maxBeforeScroll, u32 initialRow);
bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u8 maxBeforeScroll, u32 initialRow, u32 callbackSet);
bool8 ScriptMenu_Multichoice(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress);
bool8 ScriptMenu_MultichoiceWithDefault(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 defaultChoice);
bool8 ScriptMenu_YesNo(u8 left, u8 top);

View file

@ -1382,6 +1382,7 @@ bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx)
u32 maxBeforeScroll = ScriptReadByte(ctx);
bool32 shouldSort = ScriptReadByte(ctx);
u32 initialSelected = VarGet(ScriptReadHalfword(ctx));
u32 callbackSet = ScriptReadByte(ctx);
u32 initialRow = 0;
// Read vararg
u32 argc = ScriptReadByte(ctx);
@ -1426,7 +1427,7 @@ bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx)
MultichoiceDynamic_DestroyStack();
}
if (ScriptMenu_MultichoiceDynamic(left, top, argc, items, ignoreBPress, maxBeforeScroll, initialRow))
if (ScriptMenu_MultichoiceDynamic(left, top, argc, items, ignoreBPress, maxBeforeScroll, initialRow, callbackSet))
{
ScriptContext_Stop();
return TRUE;
@ -1440,7 +1441,7 @@ bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx)
bool8 ScrCmd_dynmultipush(struct ScriptContext *ctx)
{
const u8 *name = (const u8*) ScriptReadWord(ctx);
u32 id = ScriptReadByte(ctx);
u32 id = VarGet(ScriptReadHalfword(ctx));
struct ListMenuItem item = {.name = name, .id = id};
MultichoiceDynamic_PushElement(item);
}

View file

@ -16,6 +16,7 @@
#include "list_menu.h"
#include "malloc.h"
#include "util.h"
#include "item_icon.h"
#include "constants/field_specials.h"
#include "constants/items.h"
#include "constants/script_menu.h"
@ -23,8 +24,26 @@
#include "data/script_menu.h"
struct DynamicListMenuEventArgs
{
struct ListMenuTemplate *list;
u16 selectedItem;
u8 windowId;
};
typedef void (*DynamicListCallback)(struct DynamicListMenuEventArgs *eventArgs);
struct DynamicListMenuEventCollection
{
DynamicListCallback OnInit;
DynamicListCallback OnSelectionChanged;
DynamicListCallback OnDestroy;
};
static EWRAM_DATA u8 sProcessInputDelay = 0;
static EWRAM_DATA u8 sDynamicMenuEventId = 0;
static EWRAM_DATA struct DynamicMultichoiceStack *sDynamicMultiChoiceStack = NULL;
static EWRAM_DATA u16 *sDynamicMenuEventScratchPad = NULL;
static u8 sLilycoveSSTidalSelections[SSTIDAL_SELECTION_COUNT];
@ -33,7 +52,7 @@ static void Task_HandleScrollingMultichoiceInput(u8 taskId);
static void Task_HandleMultichoiceInput(u8 taskId);
static void Task_HandleYesNoInput(u8 taskId);
static void Task_HandleMultichoiceGridInput(u8 taskId);
static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u32 initialRow, u8 maxBeforeScroll);
static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u32 initialRow, u8 maxBeforeScroll, u32 callbackSet);
static void DrawMultichoiceMenu(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 cursorPos);
static void InitMultichoiceCheckWrap(bool8 ignoreBPress, u8 count, u8 windowId, u8 multichoiceId);
static void DrawLinkServicesMultichoiceMenu(u8 multichoiceId);
@ -42,6 +61,28 @@ static void CreateLilycoveSSTidalMultichoice(void);
static bool8 IsPicboxClosed(void);
static void CreateStartMenuForPokenavTutorial(void);
static void InitMultichoiceNoWrap(bool8 ignoreBPress, u8 unusedCount, u8 windowId, u8 multichoiceId);
static void MultichoiceDynamicEventDebug_OnInit(struct DynamicListMenuEventArgs *eventArgs);
static void MultichoiceDynamicEventDebug_OnSelectionChanged(struct DynamicListMenuEventArgs *eventArgs);
static void MultichoiceDynamicEventDebug_OnDestroy(struct DynamicListMenuEventArgs *eventArgs);
static void MultichoiceDynamicEventShowItem_OnInit(struct DynamicListMenuEventArgs *eventArgs);
static void MultichoiceDynamicEventShowItem_OnSelectionChanged(struct DynamicListMenuEventArgs *eventArgs);
static void MultichoiceDynamicEventShowItem_OnDestroy(struct DynamicListMenuEventArgs *eventArgs);
static const struct DynamicListMenuEventCollection sDynamicListMenuEventCollections[] =
{
[DYN_MULTICHOICE_CB_DEBUG] =
{
.OnInit = MultichoiceDynamicEventDebug_OnInit,
.OnSelectionChanged = MultichoiceDynamicEventDebug_OnSelectionChanged,
.OnDestroy = MultichoiceDynamicEventDebug_OnDestroy
},
[DYN_MULTICHOICE_CB_SHOW_ITEM] =
{
.OnInit = MultichoiceDynamicEventShowItem_OnInit,
.OnSelectionChanged = MultichoiceDynamicEventShowItem_OnSelectionChanged,
.OnDestroy = MultichoiceDynamicEventShowItem_OnDestroy
}
};
static const struct ListMenuTemplate sScriptableListMenuTemplate =
{
@ -55,7 +96,7 @@ static const struct ListMenuTemplate sScriptableListMenuTemplate =
.fontId = FONT_NORMAL,
};
bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u8 maxBeforeScroll, u32 initialRow)
bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u8 maxBeforeScroll, u32 initialRow, u32 callbackSet)
{
if (FuncIsActiveTask(Task_HandleMultichoiceInput) == TRUE)
{
@ -65,7 +106,7 @@ bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuIte
else
{
gSpecialVar_Result = 0xFF;
DrawMultichoiceMenuDynamic(left, top, argc, items, ignoreBPress, initialRow, maxBeforeScroll);
DrawMultichoiceMenuDynamic(left, top, argc, items, ignoreBPress, initialRow, maxBeforeScroll, callbackSet);
return TRUE;
}
}
@ -98,6 +139,74 @@ bool8 ScriptMenu_MultichoiceWithDefault(u8 left, u8 top, u8 multichoiceId, bool8
}
}
static void MultichoiceDynamicEventDebug_OnInit(struct DynamicListMenuEventArgs *eventArgs)
{
DebugPrintf("OnInit: %d", eventArgs->windowId);
}
static void MultichoiceDynamicEventDebug_OnSelectionChanged(struct DynamicListMenuEventArgs *eventArgs)
{
DebugPrintf("OnSelectionChanged: %d", eventArgs->selectedItem);
}
static void MultichoiceDynamicEventDebug_OnDestroy(struct DynamicListMenuEventArgs *eventArgs)
{
DebugPrintf("OnDestroy: %d", eventArgs->windowId);
}
#define sAuxWindowId sDynamicMenuEventScratchPad[0]
#define sItemSpriteId sDynamicMenuEventScratchPad[1]
#define TAG_CB_ITEM_ICON 3000
static void MultichoiceDynamicEventShowItem_OnInit(struct DynamicListMenuEventArgs *eventArgs)
{
struct WindowTemplate *template = &gWindows[eventArgs->windowId].window;
u32 baseBlock = template->baseBlock + template->width * template->height;
struct WindowTemplate auxTemplate = CreateWindowTemplate(0, template->tilemapLeft + template->width + 2, template->tilemapTop, 4, 4, 15, baseBlock);
u32 auxWindowId = AddWindow(&auxTemplate);
SetStandardWindowBorderStyle(auxWindowId, FALSE);
FillWindowPixelBuffer(auxWindowId, 0x11);
CopyWindowToVram(auxWindowId, COPYWIN_FULL);
sAuxWindowId = auxWindowId;
sItemSpriteId = MAX_SPRITES;
}
static void MultichoiceDynamicEventShowItem_OnSelectionChanged(struct DynamicListMenuEventArgs *eventArgs)
{
struct WindowTemplate *template = &gWindows[eventArgs->windowId].window;
u32 x = template->tilemapLeft * 8 + template->width * 8 + 36;
u32 y = template->tilemapTop * 8 + 20;
if (sItemSpriteId != MAX_SPRITES)
{
FreeSpriteTilesByTag(TAG_CB_ITEM_ICON);
FreeSpritePaletteByTag(TAG_CB_ITEM_ICON);
DestroySprite(&gSprites[sItemSpriteId]);
}
sItemSpriteId = AddItemIconSprite(TAG_CB_ITEM_ICON, TAG_CB_ITEM_ICON, eventArgs->selectedItem);
gSprites[sItemSpriteId].oam.priority = 0;
gSprites[sItemSpriteId].x = x;
gSprites[sItemSpriteId].y = y;
}
static void MultichoiceDynamicEventShowItem_OnDestroy(struct DynamicListMenuEventArgs *eventArgs)
{
ClearStdWindowAndFrame(sAuxWindowId, TRUE);
RemoveWindow(sAuxWindowId);
if (sItemSpriteId != MAX_SPRITES)
{
FreeSpriteTilesByTag(TAG_CB_ITEM_ICON);
FreeSpritePaletteByTag(TAG_CB_ITEM_ICON);
DestroySprite(&gSprites[sItemSpriteId]);
}
}
#undef sAuxWindowId
#undef sItemSpriteId
#undef TAG_CB_ITEM_ICON
static void FreeListMenuItems(struct ListMenuItem *items, u32 count)
{
u32 i;
@ -226,10 +335,15 @@ static void MultichoiceDynamic_MoveCursor(s32 itemIndex, bool8 onInit, struct Li
if (taskId != TASK_NONE)
{
ListMenuGetScrollAndRow(gTasks[taskId].data[0], &gScrollableMultichoice_ScrollOffset, NULL);
if (sDynamicMenuEventId != DYN_MULTICHOICE_CB_NONE && sDynamicListMenuEventCollections[sDynamicMenuEventId].OnSelectionChanged && !onInit)
{
struct DynamicListMenuEventArgs eventArgs = {.selectedItem = itemIndex, .windowId = list->template.windowId, .list = &list->template};
sDynamicListMenuEventCollections[sDynamicMenuEventId].OnSelectionChanged(&eventArgs);
}
}
}
static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u32 initialRow, u8 maxBeforeScroll)
static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u32 initialRow, u8 maxBeforeScroll, u32 callbackSet)
{
u32 i;
u8 windowId;
@ -250,6 +364,16 @@ static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenu
SetStandardWindowBorderStyle(windowId, FALSE);
CopyWindowToVram(windowId, COPYWIN_FULL);
// I don't like this being global either, but I could not come up with another solution that
// does not invade the whole ListMenu infrastructure.
sDynamicMenuEventId = callbackSet;
sDynamicMenuEventScratchPad = AllocZeroed(100 * sizeof(u16));
if (sDynamicMenuEventId != DYN_MULTICHOICE_CB_NONE && sDynamicListMenuEventCollections[sDynamicMenuEventId].OnInit)
{
struct DynamicListMenuEventArgs eventArgs = {.selectedItem = initialRow, .windowId = windowId, .list = NULL};
sDynamicListMenuEventCollections[sDynamicMenuEventId].OnInit(&eventArgs);
}
gMultiuseListMenuTemplate = sScriptableListMenuTemplate;
gMultiuseListMenuTemplate.windowId = windowId;
gMultiuseListMenuTemplate.items = items;
@ -266,6 +390,12 @@ static void DrawMultichoiceMenuDynamic(u8 left, u8 top, u8 argc, struct ListMenu
StoreWordInTwoHalfwords(&gTasks[taskId].data[3], (u32) items);
list = (void *) gTasks[gTasks[taskId].data[0]].data;
ListMenuChangeSelectionFull(list, TRUE, FALSE, initialRow, TRUE);
if (sDynamicMenuEventId != DYN_MULTICHOICE_CB_NONE && sDynamicListMenuEventCollections[sDynamicMenuEventId].OnSelectionChanged)
{
struct DynamicListMenuEventArgs eventArgs = {.selectedItem = items[initialRow].id, .windowId = windowId, .list = &gMultiuseListMenuTemplate};
sDynamicListMenuEventCollections[sDynamicMenuEventId].OnSelectionChanged(&eventArgs);
}
ListMenuGetScrollAndRow(gTasks[taskId].data[0], &gScrollableMultichoice_ScrollOffset, NULL);
if (argc > maxBeforeScroll)
{
@ -377,6 +507,15 @@ static void Task_HandleScrollingMultichoiceInput(u8 taskId)
struct ListMenuItem *items;
PlaySE(SE_SELECT);
if (sDynamicMenuEventId != DYN_MULTICHOICE_CB_NONE && sDynamicListMenuEventCollections[sDynamicMenuEventId].OnDestroy)
{
struct DynamicListMenuEventArgs eventArgs = {.selectedItem = input, .windowId = gTasks[taskId].data[2], .list = NULL};
sDynamicListMenuEventCollections[sDynamicMenuEventId].OnDestroy(&eventArgs);
}
sDynamicMenuEventId = DYN_MULTICHOICE_CB_NONE;
if (gTasks[taskId].data[5] > gTasks[taskId].data[7])
{
RemoveScrollIndicatorArrowPair(gTasks[taskId].data[6]);
@ -384,7 +523,7 @@ static void Task_HandleScrollingMultichoiceInput(u8 taskId)
LoadWordFromTwoHalfwords(&gTasks[taskId].data[3], (u32* )(&items));
FreeListMenuItems(items, gTasks[taskId].data[5]);
TRY_FREE_AND_SET_NULL(sDynamicMenuEventScratchPad);
DestroyListMenuTask(gTasks[taskId].data[0], NULL, NULL);
ClearStdWindowAndFrame(gTasks[taskId].data[2], TRUE);
RemoveWindow(gTasks[taskId].data[2]);