Male
Seen 21 Hours Ago
Posted 2 Weeks Ago
91 posts
13.5 Years
Dynamic Multichoice

Hey there. Over the last couple days I've been working on a take on the multichoice script command that allows you to create arbitrary menus from the scripting system. I was mostly not fully satisfied with the implementations of list menus / other forms of dynamic multichoice versions, so I decided to overengineer one. The idea is to have a "satisfies all needs" solution that can do all of these things:
  • Implement simple menus in one line without needing to adjust a global list.
  • Implement "conditions" (see below) to create menus based on certain game states.
  • Make drawing the list customizable, if you are willing to put in a little more coding effort.

Examples



How to implement

All the code for this system is available on my pokeemerald fork: https://github.com/SBird1337/pokeemerald/tree/feature/dynmulti
If you want to use it you can choose to implement it however you want. (Yes, you can also use git to merge it.)
I would really recommend looking at the changes I made and try to follow along by implementing them yourself on your fork.

Basic Usage

The most simple menus you can build as seen in the examples above just consist of a list of elements, you can use the following syntax in poryscript:
dynmultichoice(left, top, ignoreBPress, maxBeforeScroll, initialSelected, callbacks, "Option 1", "Option 2", "Option 3", ...)
with the following arguments:
  • left: The x offset of the menu (in tiles / units of 8 pixels)
  • top (the y offset of the menu, in tiles / units of 8 pixels)
  • ignoreBPress: Whether the menu should stay open if the user presses the B Button
  • maxBeforeScroll: The maximum amount of items shown before the menu scrolls
  • initialSelected: Variable or static value that determines the initially selected item
  • callbacks: The event callbacks of the menu. For a simple menu supply DYN_MULTICHOICE_CB_NONE
  • ...: Any number of menu options can follow after these arguments

The menu will automatically convert to a scrolling list (and indicate that with UI Arrows) if the amount of elements is larger than maxBeforeScroll.

When the menu is closed by player interaction, it will return the selected option index in VAR_RESULT. If the user aborted the menu by pressing the B Button, it will return 127.

Conditional Menus

Ever thought you only wanted to enable certain options if the player aquired a specific item, or progressed the game to a certain point? You can use this pattern to fully customize the options of your menu:
dynmultipush("First option", 0)
if (flag(FLAG_SYS_GAME_CLEAR)) {
   dynmultipush("Secret option", 1)
}
dynmultistack(left, top, ignoreBPress, maxBeforeScroll, shouldSort, initialSelected, callbacks)
dynmultistack behaves just like dynmultichoice with the addition of a shouldSort argument, that determines whether the list should be sorted according to the IDs you pass to dynmultipush. The latter also accepts variables as its second argument.

Callbacks

The callbacks argument chooses from a set of event listeners. If that does not make any sense to you, you may notice in the example I already use one such set of events, which will display an icon corresponding to the item id which the menu is built from. But you do not need to use the callbacks, you can always pass DYN_MULTICHOICE_CB_NONE. Those callbacks are written in C and allow you to have a very powerful extensions for those menus that really need it. You might also want to look at examples over at https://github.com/SBird1337/pokeemerald/blob/feature/dynmulti/src/script_menu.c#L71-L85.

Script Example

The example images from above where created by using the following script in poryscript syntax:
script EventScript_MultichoiceTests {
    lock 
    faceplayer
    msgbox (LittlerootTown_Text_CanUsePCToStoreItems, MSGBOX_DEFAULT)

    // random items with visualization example

    random(ITEMS_COUNT)
    bufferitemname(STR_VAR_1, VAR_RESULT)
    dynmultipush("{STR_VAR_1}", VAR_RESULT)
    random(ITEMS_COUNT)
    bufferitemname(STR_VAR_2, VAR_RESULT)
    dynmultipush("{STR_VAR_2}", VAR_RESULT)
    random(ITEMS_COUNT)
    bufferitemname(STR_VAR_3, VAR_RESULT)
    dynmultipush("{STR_VAR_3}", VAR_RESULT)
    dynmultistack(0, 0, TRUE, 6, FALSE, VAR_0x800A, DYN_MULTICHOICE_CB_SHOW_ITEM)

    // inline example without scrolling

    dynmultichoice(0,0, TRUE, 6, 0, DYN_MULTICHOICE_CB_NONE, "Option 1", "Option 2", "Option 3")

    // inline example with scrolling

    dynmultichoice(0,0, TRUE, 3, 0, DYN_MULTICHOICE_CB_NONE, "Option 1", "Option 2", "Option 3", "Option 4")

    buffernumberstring(STR_VAR_1, VAR_RESULT)
    msgbox("{STR_VAR_1}", MSGBOX_DEFAULT)
    closemessage
    release
    end
}
For an example of how this would translate to regular scripting visit https://www.huderlem.com/poryscript-playground/

I hope this is helpful to some people, at least I had a blast coding it. If you have questions about it, feel free to contact me on the usual suspects of discord servers, or leave a comment (Though bear in mind I'm not very active on this forum, and I might take a while to respond)

If you want to credit me for this (Which you should not feel obligated to do) I would appreciate if you used the name SBird instead of any other aliases you might know me under.

Good luck on your scripting adventures!

~SBird
Seen 1 Week Ago
Posted 2 Weeks Ago
177 posts
5.4 Years
THIS LOOKS PHENOMENAL!!
I'LL DEFINETLY BE USING THIS ON MY PROJECT!
This not only is really easy to use, customise and display, but it's also really professional-looking and flexible! I look foward to see what I can create thanks to this!

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 11 Hours Ago
Posted 4 Days Ago
2,998 posts
14.3 Years
Dynamic Multichoice
I think you know what I think of this beautiful piece of work, but I'll say it again; it's absolutely phenomenal and it was sorely needed too.
It's super flexible and allows the user to do so. many. things with the multichoices system it's ridiculous.
Completely foolproof and futureproof, surely something that would have made the lives of the workers at Game Freak much, much easier imo.
One could easily go as far as to replace every multichoice in the code with one of these and get rid of all the multichoice arrays.

When you first linked the branch before it was ready for public use, I decided to use it to remake the start menu of the game on a whim, thinking back about MapleFang's flawed implementation.
Since I just finished getting it to a point in which I think it's good enough to distribute, I think that I may as well link it here to display one of the many possibilities this system opens

It's functionally equivalent to the original start menu as far as I tested, but naturally, it has the advantages that this system brings, like faster transition from one option to the next, being able to hold the arrow keys to move between options constantly, being easier to read and easier to expand too!
EDIT: And scrolling options, of course.

https://github.com/LOuroboros/pokeemerald/commits/dynamicMultiTest2

Note: I didn't bother trying to keep absolute accuracy on the visuals department; that's a fool's errand imo. I just left it in a state that I personally like.
Note2: I intended to delete the code that composes the original and now unused start menu too, but it's too ingrained in the codebase which makes the task a big pita. I'll leave that up to the user.