Skip to content

Commit

Permalink
[script-command] add dynmultichoice
Browse files Browse the repository at this point in the history
 * supports variable length arguments
 * automatically scrolls
 * supports building list menus from a stack
  • Loading branch information
sbird committed Jan 17, 2023
1 parent d5a4bfc commit 569fa0a
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 4 deletions.
25 changes: 25 additions & 0 deletions asm/macros/event.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,31 @@
.2byte \quantity
.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 argv:vararg
.byte 0xe3
.byte \left
.byte \top
.byte \ignoreBPress
.byte \maxBeforeScroll
.byte (.Ldynmultichoice_\@_2 - .Ldynmultichoice_\@_1) / 4
.Ldynmultichoice_\@_1:
.4byte \argv
.Ldynmultichoice_\@_2:
.endm

.macro dynmultipush name:req, id:req
.byte 0xe4
.4byte \name
.byte \id
.endm

.macro dynmultistack left:req, top:req, ignoreBPress:req, maxBeforeScroll:req
dynmultichoice \left, \top, \ignoreBPress, \maxBeforeScroll, 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
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
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,8 +1,34 @@
#ifndef GUARD_SCRIPT_MENU_H
#define GUARD_SCRIPT_MENU_H

#include "list_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);
void MultichoiceDynamic_DestroyStack(void);
bool8 ScriptMenu_MultichoiceDynamic(u8 left, u8 top, u8 argc, struct ListMenuItem *items, bool8 ignoreBPress, u8 maxBeforeScroll);
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
9 changes: 5 additions & 4 deletions src/field_specials.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,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 @@ -84,6 +84,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 @@ -2477,7 +2478,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 @@ -2551,7 +2552,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 @@ -2672,7 +2673,7 @@ static void ScrollableMultichoice_UpdateScrollArrows(u8 taskId)
template.secondY = task->tHeight * 8 + 10;
template.fullyUpThreshold = 0;
template.fullyDownThreshold = task->data[1] - task->tMaxItemsOnScreen;
task->tScrollArrowId = AddScrollIndicatorArrowPair(&template, &sScrollableMultichoice_ScrollOffset);
task->tScrollArrowId = AddScrollIndicatorArrowPair(&template, &gScrollableMultichoice_ScrollOffset);
}
}

Expand Down
87 changes: 87 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 @@ -68,6 +70,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 @@ -1350,6 +1353,90 @@ 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;
u8 left = ScriptReadByte(ctx);
u8 top = ScriptReadByte(ctx);
bool8 ignoreBPress = ScriptReadByte(ctx);
u8 maxBeforeScroll = ScriptReadByte(ctx);
// Read vararg
u8 argc = ScriptReadByte(ctx);
struct ListMenuItem *items;

if (argc == 0)
return;

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;
}
}
else
{
argc = MultichoiceDynamic_StackSize();
items = AllocZeroed(sizeof(struct ListMenuItem) * argc);
for (i = 0; i < argc; ++i)
{
u8 *nameBuffer = Alloc(100);
struct ListMenuItem *currentItem = MultichoiceDynamic_PopElement();
StringExpandPlaceholders(nameBuffer, currentItem->name);
items[i].name = nameBuffer;
items[i].id = currentItem->id;
}
DynamicMultichoiceSortList(items, argc);
MultichoiceDynamic_DestroyStack();
}

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

bool8 ScrCmd_dynmultipush(struct ScriptContext *ctx)
{
const u8 *name = (const u8*) ScriptReadWord(ctx);
u32 id = ScriptReadByte(ctx);
struct ListMenuItem item = {.name = name, .id = id};
MultichoiceDynamic_PushElement(item);
}

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

0 comments on commit 569fa0a

Please sign in to comment.