Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Multichoices #3826

Merged
merged 19 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions asm/macros/event.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,38 @@
.2byte \quantity
.endm

.macro _dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req, callbacks:req argv:vararg
.byte 0xe3
.2byte \left
.2byte \top
.byte \ignoreBPress
.byte \maxBeforeScroll
.byte \shouldSort
.2byte \initialSelected
.byte \callbacks
.byte (.Ldynmultichoice_\@_2 - .Ldynmultichoice_\@_1) / 4
.Ldynmultichoice_\@_1:
.4byte \argv
.Ldynmultichoice_\@_2:
.endm

@ 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, 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
.2byte \id
.endm

.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


@ Supplementary

Expand Down
2 changes: 2 additions & 0 deletions data/script_cmd_table.inc
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ gScriptCmdTable::
.4byte ScrCmd_warpwhitefade @ 0xe0
.4byte ScrCmd_buffercontestname @ 0xe1
.4byte ScrCmd_bufferitemnameplural @ 0xe2
.4byte ScrCmd_dynmultichoice @ 0xe3
.4byte ScrCmd_dynmultipush @ 0xe4

gScriptCmdTableEnd::
.4byte ScrCmd_nop
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
1 change: 1 addition & 0 deletions include/field_specials.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

extern bool8 gBikeCyclingChallenge;
extern u8 gBikeCollisions;
extern u16 gScrollableMultichoice_ScrollOffset;

u8 GetLeadMonIndex(void);
u8 IsDestinationBoxFull(void);
Expand Down
2 changes: 2 additions & 0 deletions include/list_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,7 @@ u8 AddScrollIndicatorArrowPair(const struct ScrollArrowsTemplate *arrowInfo, u16
u8 AddScrollIndicatorArrowPairParameterized(u32 arrowType, s32 commonPos, s32 firstPos, s32 secondPos, s32 fullyDownThreshold, s32 tileTag, s32 palTag, u16 *currItemPtr);
void RemoveScrollIndicatorArrowPair(u8 taskId);
void Task_ScrollIndicatorArrowPairOnMainMenu(u8 taskId);
bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAndCallCallback, u8 count, bool8 movingDown);
bool8 ListMenuChangeSelectionFull(struct ListMenu *list, bool32 updateCursor, bool32 callCallback, u8 count, bool8 movingDown);

#endif //GUARD_LIST_MENU_H
1 change: 1 addition & 0 deletions include/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void ScriptCall(struct ScriptContext *ctx, const u8 *ptr);
void ScriptReturn(struct ScriptContext *ctx);
u16 ScriptReadHalfword(struct ScriptContext *ctx);
u32 ScriptReadWord(struct ScriptContext *ctx);
u32 ScriptPeekWord(struct ScriptContext *ctx);
void LockPlayerFieldControls(void);
void UnlockPlayerFieldControls(void);
bool8 ArePlayerFieldControlsLocked(void);
Expand Down
26 changes: 26 additions & 0 deletions include/script_menu.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
#ifndef GUARD_SCRIPT_MENU_H
#define GUARD_SCRIPT_MENU_H

#include "list_menu.h"
#include "constants/script_menu.h"
#include "menu.h"

// The default size the stack for dynamic multichoice is initialized to
// If you try to push an element when the stack is full, it will be reallocated
// With increasing capacity of MULTICHOICE_DYNAMIC_STACK_INC

#define MULTICHOICE_DYNAMIC_STACK_SIZE 5
#define MULTICHOICE_DYNAMIC_STACK_INC 5

extern const u8 *const gStdStrings[];

struct DynamicMultichoiceStack
{
s32 top;
u32 capacity;
struct ListMenuItem *elements;
};

void MultichoiceDynamic_InitStack(u32 capacity);
void MultichoiceDynamic_ReallocStack(u32 newCapacity);
bool32 MultichoiceDynamic_StackFull(void);
bool32 MultichoiceDynamic_StackEmpty(void);
u32 MultichoiceDynamic_StackSize(void);
void MultichoiceDynamic_PushElement(struct ListMenuItem item);
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, 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);
void DrawMultichoiceMenuInternal(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 cursorPos, const struct MenuAction *actions, int count);
Expand Down
9 changes: 5 additions & 4 deletions src/field_specials.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ static EWRAM_DATA u8 sTutorMoveAndElevatorWindowId = 0;
static EWRAM_DATA u16 sLilycoveDeptStore_NeverRead = 0;
static EWRAM_DATA u16 sLilycoveDeptStore_DefaultFloorChoice = 0;
static EWRAM_DATA struct ListMenuItem *sScrollableMultichoice_ListMenuItem = NULL;
static EWRAM_DATA u16 sScrollableMultichoice_ScrollOffset = 0;

static EWRAM_DATA u16 sFrontierExchangeCorner_NeverRead = 0;
static EWRAM_DATA u8 sScrollableMultichoice_ItemSpriteId = 0;
static EWRAM_DATA u8 sBattlePointsWindowId = 0;
Expand All @@ -96,6 +96,7 @@ static EWRAM_DATA u8 sPCBoxToSendMon = 0;
static EWRAM_DATA u32 sBattleTowerMultiBattleTypeFlags = 0;

struct ListMenuTemplate gScrollableMultichoice_ListMenuTemplate;
EWRAM_DATA u16 gScrollableMultichoice_ScrollOffset = 0;

void TryLoseFansFromPlayTime(void);
void SetPlayerGotFirstFans(void);
Expand Down Expand Up @@ -2561,7 +2562,7 @@ static void Task_ShowScrollableMultichoice(u8 taskId)
struct Task *task = &gTasks[taskId];

LockPlayerFieldControls();
sScrollableMultichoice_ScrollOffset = 0;
gScrollableMultichoice_ScrollOffset = 0;
sScrollableMultichoice_ItemSpriteId = MAX_SPRITES;
FillFrontierExchangeCornerWindowAndItemIcon(task->tScrollMultiId, 0);
ShowBattleFrontierTutorWindow(task->tScrollMultiId, 0);
Expand Down Expand Up @@ -2635,7 +2636,7 @@ static void ScrollableMultichoice_MoveCursor(s32 itemIndex, bool8 onInit, struct
u16 selection;
struct Task *task = &gTasks[taskId];
ListMenuGetScrollAndRow(task->tListTaskId, &selection, NULL);
sScrollableMultichoice_ScrollOffset = selection;
gScrollableMultichoice_ScrollOffset = selection;
ListMenuGetCurrentItemArrayId(task->tListTaskId, &selection);
HideFrontierExchangeCornerItemIcon(task->tScrollMultiId, sFrontierExchangeCorner_NeverRead);
FillFrontierExchangeCornerWindowAndItemIcon(task->tScrollMultiId, selection);
Expand Down Expand Up @@ -2756,7 +2757,7 @@ static void ScrollableMultichoice_UpdateScrollArrows(u8 taskId)
template.secondY = task->tHeight * 8 + 10;
template.fullyUpThreshold = 0;
template.fullyDownThreshold = task->tNumItems - task->tMaxItemsOnScreen;
task->tScrollArrowId = AddScrollIndicatorArrowPair(&template, &sScrollableMultichoice_ScrollOffset);
task->tScrollArrowId = AddScrollIndicatorArrowPair(&template, &gScrollableMultichoice_ScrollOffset);
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/list_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ struct RedArrowCursor

// this file's functions
static u8 ListMenuInitInternal(struct ListMenuTemplate *listMenuTemplate, u16 scrollOffset, u16 selectedRow);
static bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAndCallCallback, u8 count, bool8 movingDown);
static void ListMenuPrintEntries(struct ListMenu *list, u16 startIndex, u16 yOffset, u16 count);
static void ListMenuDrawCursor(struct ListMenu *list);
static void ListMenuCallSelectionChangedCallback(struct ListMenu *list, u8 onInit);
Expand Down Expand Up @@ -837,7 +836,7 @@ static void ListMenuScroll(struct ListMenu *list, u8 count, bool8 movingDown)
}
}

static bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAndCallCallback, u8 count, bool8 movingDown)
bool8 ListMenuChangeSelectionFull(struct ListMenu *list, bool32 updateCursor, bool32 callCallback, u8 count, bool8 movingDown)
{
u16 oldSelectedRow;
u8 selectionChange, i, cursorCount;
Expand All @@ -857,7 +856,7 @@ static bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAn
} while (list->template.items[list->scrollOffset + list->selectedRow].id == LIST_HEADER);
}

if (updateCursorAndCallCallback)
if (updateCursor)
{
switch (selectionChange)
{
Expand All @@ -867,15 +866,17 @@ static bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAn
case 1:
ListMenuErasePrintedCursor(list, oldSelectedRow);
ListMenuDrawCursor(list);
ListMenuCallSelectionChangedCallback(list, FALSE);
if (callCallback)
ListMenuCallSelectionChangedCallback(list, FALSE);
CopyWindowToVram(list->template.windowId, COPYWIN_GFX);
break;
case 2:
case 3:
ListMenuErasePrintedCursor(list, oldSelectedRow);
ListMenuScroll(list, cursorCount, movingDown);
ListMenuDrawCursor(list);
ListMenuCallSelectionChangedCallback(list, FALSE);
if (callCallback)
ListMenuCallSelectionChangedCallback(list, FALSE);
CopyWindowToVram(list->template.windowId, COPYWIN_GFX);
break;
}
Expand All @@ -884,6 +885,11 @@ static bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAn
return FALSE;
}

bool8 ListMenuChangeSelection(struct ListMenu *list, bool8 updateCursorAndCallCallback, u8 count, bool8 movingDown)
{
return ListMenuChangeSelectionFull(list, updateCursorAndCallCallback, updateCursorAndCallCallback, count, movingDown);
}

static void ListMenuCallSelectionChangedCallback(struct ListMenu *list, u8 onInit)
{
if (list->template.moveCursorFunc != NULL)
Expand Down
98 changes: 98 additions & 0 deletions src/scrcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
#include "trainer_see.h"
#include "tv.h"
#include "window.h"
#include "list_menu.h"
#include "malloc.h"
#include "constants/event_objects.h"

typedef u16 (*SpecialFunc)(void);
Expand All @@ -69,6 +71,7 @@ extern const u8 *gStdScripts[];
extern const u8 *gStdScripts_End[];

static void CloseBrailleWindow(void);
static void DynamicMultichoiceSortList(struct ListMenuItem *items, u32 count);

// This is defined in here so the optimizer can't see its value when compiling
// script.c.
Expand Down Expand Up @@ -1351,6 +1354,101 @@ bool8 ScrCmd_yesnobox(struct ScriptContext *ctx)
}
}

static void DynamicMultichoiceSortList(struct ListMenuItem *items, u32 count)
{
u32 i,j;
struct ListMenuItem tmp;
for (i = 0; i < count - 1; ++i)
{
for (j = 0; j < count - i - 1; ++j)
{
if (items[j].id > items[j+1].id)
{
tmp = items[j];
items[j] = items[j+1];
items[j+1] = tmp;
}
}
}
}

#define DYN_MULTICHOICE_DEFAULT_MAX_BEFORE_SCROLL 6

bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx)
{
u32 i;
u32 left = VarGet(ScriptReadHalfword(ctx));
u32 top = VarGet(ScriptReadHalfword(ctx));
bool32 ignoreBPress = ScriptReadByte(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);
struct ListMenuItem *items;

if (argc == 0)
return FALSE;

if (maxBeforeScroll == 0xFF)
maxBeforeScroll = DYN_MULTICHOICE_DEFAULT_MAX_BEFORE_SCROLL;

if ((const u8*) ScriptPeekWord(ctx) != NULL)
{
items = AllocZeroed(sizeof(struct ListMenuItem) * argc);
for (i = 0; i < argc; ++i)
{
u8 *nameBuffer = Alloc(100);
const u8 *arg = (const u8 *) ScriptReadWord(ctx);
StringExpandPlaceholders(nameBuffer, arg);
items[i].name = nameBuffer;
items[i].id = i;
if (i == initialSelected)
initialRow = i;
}
}
else
{
argc = MultichoiceDynamic_StackSize();
items = AllocZeroed(sizeof(struct ListMenuItem) * argc);
for (i = 0; i < argc; ++i)
{
struct ListMenuItem *currentItem = MultichoiceDynamic_PeekElementAt(i);
items[i] = *currentItem;
if (currentItem->id == initialSelected)
initialRow = i;
}
if (shouldSort)
DynamicMultichoiceSortList(items, argc);
MultichoiceDynamic_DestroyStack();
}

if (ScriptMenu_MultichoiceDynamic(left, top, argc, items, ignoreBPress, maxBeforeScroll, initialRow, callbackSet))
{
ScriptContext_Stop();
return TRUE;
}
else
{
return FALSE;
}
}

bool8 ScrCmd_dynmultipush(struct ScriptContext *ctx)
{
u8 *nameBuffer = Alloc(100);
const u8 *name = (const u8*) ScriptReadWord(ctx);
u32 id = VarGet(ScriptReadHalfword(ctx));
struct ListMenuItem item;
StringExpandPlaceholders(nameBuffer, name);
item.name = nameBuffer;
item.id = id;
MultichoiceDynamic_PushElement(item);
return FALSE;
}

bool8 ScrCmd_multichoice(struct ScriptContext *ctx)
{
u8 left = ScriptReadByte(ctx);
Expand Down
9 changes: 9 additions & 0 deletions src/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ u32 ScriptReadWord(struct ScriptContext *ctx)
return (((((value3 << 8) + value2) << 8) + value1) << 8) + value0;
}

u32 ScriptPeekWord(struct ScriptContext *ctx)
{
u32 value0 = *(ctx->scriptPtr);
u32 value1 = *(ctx->scriptPtr + 1);
u32 value2 = *(ctx->scriptPtr + 2);
u32 value3 = *(ctx->scriptPtr + 3);
return (((((value3 << 8) + value2) << 8) + value1) << 8) + value0;
}

void LockPlayerFieldControls(void)
{
sLockFieldControls = TRUE;
Expand Down
Loading
Loading