Skip to content

Commit

Permalink
[script-command, dynmultichoice] implement event handler
Browse files Browse the repository at this point in the history
  • Loading branch information
sbird committed Jan 17, 2023
1 parent 276ce62 commit a7cd4ca
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 14 deletions.
13 changes: 7 additions & 6 deletions asm/macros/event.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1729,14 +1729,15 @@
.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
.byte \ignoreBPress
.byte \maxBeforeScroll
.byte \shouldSort
.2byte \initialSelected
.byte \callbacks
.byte (.Ldynmultichoice_\@_2 - .Ldynmultichoice_\@_1) / 4
.Ldynmultichoice_\@_1:
.4byte \argv
Expand All @@ -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


Expand Down
6 changes: 6 additions & 0 deletions include/constants/script_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion include/script_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/scrcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down
149 changes: 144 additions & 5 deletions src/script_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,34 @@
#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"
#include "constants/songs.h"

#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];

Expand All @@ -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);
Expand All @@ -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 =
{
Expand All @@ -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)
{
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -377,14 +507,23 @@ 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]);
}

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]);
Expand Down

0 comments on commit a7cd4ca

Please sign in to comment.