"Lobby" menus discussion / Pause menu research

Hello y’all :wave:

Basically, I’m trying to recreate this kind of menu:

Here’s what I did:
First, I found Vespura’s fivem-frontend-api/FrontendMenu.cs at master · DevTestingPizza/fivem-frontend-api · GitHub, and I tried, somewhat successful, to port it to Lua.

I used “FE_MENU_VERSION_CORONA” as the frontend menu, and the scaleforms provided by vespura to achieve this:

So far so good. You can use the mouse or keyboard on the buttons natively, but I think it will be easier to make my own “selection”.

And now the problem:
I…I don’t know where to find the scaleform functions. Sure, buttons and players are showing correctly, but the details page at least does not want to populate at all for some reason.

Another issue is the subtitle. If I add it, the whole header breaks. I think it has to do with the player card on the right, but I don’t know.

Any suggestions on those issues are more than welcomed.

My intensions:

  • Kinda want to make a menu framework for this. It looks cool and I want it :smiley:
  • Completly changing the current pause menu with this could also look cool.
6 Likes

Thank god I’m working from home :slight_smile:

Update

  • Fixed header text distortion. It was caused by some weird boolean in SET_HEADER_TITLE that the scripts always refered as ‘true’…it should have been false.

  • Found a way to remove the player details at top-right, using “SHOW_HEADING_DETAILS”.

  • Found " SET_HEADING_DETAILS()" that let’s you modify the cash, time and player name at top right, but keep in mind that the game will re-update this almost immediately… so it’s only useful if you have fixed time.

  • FINALLY GOT THE DETAILS CARD WORKING! It uses “SET_COLUMN_TITLE” to set up… but honestly I haven’t found any reliable params for it, so I’m just throwing random variables and see what sticks.
    That Warning at the bottom works on the same scaleform.

3 Likes

Some progress was made:

  • Found a way to update the header text and colors. You can also lock the tabs if you want. Scaleforms used: SET_MENU_HEADER_TEXT_BY_INDEX, SET_ALL_HIGHLIGHTS(for the black color on the header) and SET_MENU_ITEM_COLOUR(for the blue / white strip above the header).
  • Header alerts are also a thing. Works and looks like the the player status. They are possible with SET_MENU_ITEM_ALERT scaleform.

Things still needed:

  • A way to update the “mission image”. I tried with TXD_ALREADY_LOADED, TXD_HAS_LOADED and ADD_TXD_REF_RESPONSE but it was unsuccessful.
  • We need to find a way to retrieve the input from the pause menu. Or at least the button selection. Right now it’s…kind of a useless screen.
1 Like

Worst case these are directly bound to cloud textures.

There’s a series of natives supposedly involved in this.

That…actually works. It returns a “lastMenuID”, which seems to be -1000 on the lobby menu, and a “selectedUniqueID” which is exactly what we need. The Unique ID is a param that we can set when generating the scaleform.

PushScaleformMovieFunctionN("SET_DATA_SLOT");
        PushScaleformMovieFunctionParameterInt(0); --// column
        PushScaleformMovieFunctionParameterInt(i); --// index
        PushScaleformMovieFunctionParameterInt(0); --// menu ID 0
        PushScaleformMovieFunctionParameterInt(i); --// unique ID 0
        PushScaleformMovieFunctionParameterInt(1); --// type 0
        PushScaleformMovieFunctionParameterInt(0); --// initialIndex 0
        PushScaleformMovieFunctionParameterBool(true); --// isSelectable true
        PushScaleformMovieFunctionParameterString(k.text);
        PushScaleformMovieFunctionParameterString(k.thing); --rightText usually
        --///// UNSURE HOW THIS WORKS, BUT IF YOU UNCOMMENT THIS, IT'LL ADD AN ICON TO THE ROW.
        --///// MAKING THE STRING "20" AND THE BOOL TRUE SEEMS TO DO SOMETHING WITH A ROCKSTAR LOGO INSTEAD.
        -- CritteR's note: I can't get this to work, even with Vespura's notes.
        PushScaleformMovieFunctionParameterInt(0);
        PushScaleformMovieFunctionParameterString(k.string); 
        PushScaleformMovieFunctionParameterInt(0);
        PushScaleformMovieFunctionParameterBool(k.bool); --// SOMETHING WITH ROCKSTAR/STAR LOGO SWITCHING.
        PopScaleformMovieFunctionVoid();

@Jaymo I believe you touched frontend stuff recently too, maybe some worthwhile info obtainable here?

I uploaded the resource to GitHub - CritteRo/fivem-lobby-pausemenu: Lobby style pause menu research resource if you want to check for yourself.

Found a couple of nice things:

  • TriggerScreenblurFadeIn(1) and TriggerScreenblurFadeOut(1000) are used to create that “menu screen blur” effect, but I’m sure you need to pair to other screen effects in order to get the original MP_PAUSE background feel.
  • Managed to get R* and mission type icons on the left-side buttons.
  • Made a “proof of concept” command for button press. (/pressfr command. “Enter” by default)

The menu is pretty much done:

  • Found 2 new string params in SET_COLUMN_TITLE scaleform. Turns, those are textureDirectory and textureName!!
    It’s suited for 256x128 images, otherwise it gets stretched. Works with existing texture files, streamed .YTD, and even with some CreateRuntimeTextureFromImage() action.

This will be my final update here. Expect a framework release soon.

Github, btw: GitHub - CritteRo/fivem-lobby-pausemenu: Lobby style pause menu research resource

1 Like

Excellent work! I tested some things with a menu like this month ago but never got the Image to work!
Thanks for all the work for the community!

1 Like

Glad you like it! It was a very fun resource to work on.

I found even more settings for the frontend menu, that could not get into this specific menu, like locking tabs, or the big textbox that you can see on the “Info” tab on the pause menu.

Also, I have reason to believe that all frontend menus are split into 3 columns. Don’t know what to do with this information yet, but it’s definitely good to know.

Next mission:
research_char_creation.lua (3.0 KB)

1 Like

Sorry, I only just got the @ now due to being overwhelmed with other forum notifications. I haven’t really seen any detailed explanations of the frontend menu system so I guess I’ll start from the beginning.

Within decompiled scaleform files, the term “pausemenu” is what natives / game script call “frontend” menu as essentially, they are the same UI but the actual in game pause menu is handled by the engine whereas “frontend” is manipulated by game scripts. This can cause much confusion as scaleform makes no mention of “frontend” except for some numerical control constants whereas natives have distinct pause menu functions as well as frontend ones.

When a frontend menu is being loaded (usually when ACTIVATE_FRONTEND_MENU is called) the game uses data loaded from the data/ui/pausemenu.xml file to figure out what functions it will call the pausemenu/frontend scaleforms with. The pause menu version being loaded determines what “screen” will be loaded initially, what “header” to use, the flags which the scaleform and UI backend within the game engine will use, the “pause menu contexts” to initially activate and the hud colour to be used on all menu items by default.

For the example frontend shown here, everything below the SETTINGS - PLAYERS - DETAILS bar is considered the frontend itself whereas that bar and all above is considered the “frontend header” which you control with the BEGIN_SCALEFORM_MOVIE_METHOD_ON_FRONTEND_HEADER native. Internally, the header is just another part of the root frontend menu object and some methods you call within the frontend will call methods on the header.

A menu “screen” will specify a GFx file to load, said GFx file will contain a class that extends com.rockstargames.gtav.pauseMenuPages.PauseMenuBasePage called the menu “page”. Like all other scaleforms, when this file is loaded, it will set the TIMELINE variable to an instance of said class however it also calls the method LOADED_PAGE on its 3rd level parent which will be a class that extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuContentBase. Only one such class exists, that being com.rockstargames.gtav.pauseMenu.PAUSE_MENU_SP_CONTENT.

A menu “page” defines what menu “columns” to load, each column being a class that extends com.rockstargames.gtav.pauseMenu.pauseComponents.PauseMenuComponentBase. If a column wants to take advantage of the data slot interface, it needs to contain a “model” that extends com.rockstargames.ui.components.GUIModel which contains a “view” that extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuViewBase. The view is then responsible for creating instances of and arranging the "item"s contained within it. The class for said item extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuBaseItem.

Succinctly, a screen loads a page that contains 1 or more components that in this context are known as columns. Components that implement data slots have a model that contains an instance of a view that contains 1 or more items.

The class inheritance is very important here to understand where the data goes when you call, for example, the SET_DATA_SLOT method as the parameters get passed down through many layers of abstraction.

The parameters to SET_DATA_SLOT and UPDATE_SLOT are as follows however, some item implementations also use some of these “standard” parameters.

Argument Index Name Type Description
0 Column Index INT The column containing the slot to set the data of.
1 slotIndex INT The index of slot to set the data of.
2 menuID INT Used to keep track of what item of the pause menu this slot belongs to. You should use IDs from com.rockstargames.gtav.constants.PauseMenuLUT where it makes sense to do so.
3 uniqueID INT Used to uniquely identify items within a view. You can really use anything you want however, integers greater than 1000 can have special functions and execute actions when clicked that you’d otherwise not want to happen so, to be safe, don’t use anything greater than 1000.
4 type INT The subtype of this item.
5 initialIndex INT The initial index to show in a list item.
6 isSelectable BOOL A boolean controlling if the item can be selected. Unselectable items are greyed out when displayed and skipped over in navigation.
7 onwards Any Additional data used by the implementation of the item.

The process of figuring out what item implementations are used by what page gets annoying if you don’t have all the decompiled scaleform script easily searchable so I recommend you extract all actionscript from all of the scaleforms and put it into one directory you can search through. You also need to reference pausemenu.xml but since we’ll be doing this in FiveM, you want to use the file located in your FiveM Application Data at citizen\common\data\ui\pausemenu.xml.

I haven’t exactly got it down to a science yet but when a screen loads, it loads every <Item> with it. If those items have their <MenuAction> set as MENU_OPTION_ACTION_LINK then, the screen corresponding to the <MenuUniqueId> will also be loaded. The menu screen <depth> tag helps in this as <depth> = 0 screens are almost always headers, <depth> = 1 screens are columns and <depth> = 2 screens are context menus, the ones that contain options to kick players and such.

As an example, we’ll take a look at FE_MENU_VERSION_CORONA. So, we go from <InitialScreen>MENU_UNIQUE_ID_HEADER_CORONA</InitialScreen> to a <Item> <cTextId>PM_SCR_SET</cTextId> <MenuAction>MENU_OPTION_ACTION_LINK</MenuAction> <MenuUniqueId>MENU_UNIQUE_ID_CORONA_SETTINGS</MenuUniqueId> </Item> to a <cGfxFilename>PAUSE_MENU_PAGES_CORONA</cGfxFilename> meaning the file containing the page for the initial state of the menu version FE_MENU_VERSION_CORONA is pause_menu_pages_corona.swf.

The first frame of that GFx file puts an instance of com.rockstargames.gtav.pauseMenuPages.PAUSE_MENU_PAGES_CORONA onto the TIMELINE so that’s our page!

The setupPage function of this page looks like this:

function setupPage()
{
	super.setupPage();
	this.column1 = this.addColumn("freemodeList",1,0);
	this.column2 = this.addColumn("freemodeDetails",2,580);
	this.column3 = this.addColumn("freemodeMap",3,580);
	this.column4 = this.addColumn("mpFriendsList",4,290);
	this.setupColumns(this.column1,this.column2,this.column3,this.column4);
	this.setupColScroll([this.column1,this.column2,this.column3,this.column4],[true,false,false,false]);
}

The column at index 0 is an instance of the class registered as "freemodeList" and is offset by 0 on the x-axis. Column index 1 is a "freemodeDetails" instance offset by 580, appearing in the 3rd column visually. Column index 2 is a "freemodeMap" instance also appearing in the 3rd column, underneath the first component. Column index 3 is a "mpFriendsList" instance appearing in the 2nd column visually.

These strings, internally called linkageIDs, point to whichever class is registered with Object.registerClass(linkageID, class). For this example, here are all referenced classes and their actual names:
"freemodeList"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREE_MODE
"freemodeDetails"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREEMODE_DETAILS
"freemodeMap"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREEMODE_MAP
"mpFriendsList"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MP_MENU_FRIENDS_LIST
"freemodeListItem"com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeItem

This is the INITIALISE function of component for column 0:

function INITIALISE(mc)
{
    // [snip]
	this.model = new com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeModel();
	this.model.createView(0,{id:0,x:0,y:0,rowSpacing:2,columnSpacing:0,container:this.CONTENT,linkage:["freemodeListItem"],visibleItems:16,selectstyle:com.rockstargames.ui.components.GUIView.SCROLL_SELECTSTYLE});
    // [snip]
}

For the 0th column, that has a this.model of com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeModel with a view that uses the class registered as "freemodeListItem" as it’s item class.

Looking into com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeItem, we can finally see how the parameters we pass to SET_DATA_SLOT actually get rendered and used!

Within said class, the function this.__get__data() gets an array of the parameters after argument 6 and other functions refer to previously documented names of the class. The interesting stuff is within the __set__data(_d) override. We can see that argument 7 is the itemLeftText, argument 8 controls the verifiedMC (verified [badge] Movie Clip) and so on.

Now that we know how to get our items to display however we want, how do we tell when the player has selected an item and most importantly which item they’ve selected? I discovered some natives through decompiled scripts to help with such which were already shared however, I’ve now also discovered where that data comes from.

When scaleform calls the game interface method LAYOUT_CHANGED or LAYOUT_CHANGED_FOR_SCRIPT_ONLY, the native 0x2E22FEFA0100275E is set to true for one frame and the native 0x7E17BE53E1AAABAF will return the 1st, 3rd and 2nd parameters to the game interface call in specifically that order. Similarly, when TRIGGER_PAUSE_MENU_EVENT is called, the native 0xF284AC67940C6812 will return true for one frame and according to decompiled scripts, the native 0x36C1451A88A09630 should be used to get the 1st and 3rd parameters to the game interface call however, 0x7E17BE53E1AAABAF works all the same.

0x7E17BE53E1AAABAF's parameters are effectively:

void 0x7E17BE53E1AAABAF(int* previousMenuState, int* selectedItemMenuId, int* selectedItemUniqueId);

// previousMenuState: The previous menuState of the pause menu - 1000. The previous menuState is usually set to the menuID of the last selected item.
// selectedItemMenuId: The menuID - 1000 of the currently selected item. Is -1 if the menu state wasn't changed since the last trigger.
// selectedItemUniqueId: The uniqueID of the currently selected item.

This is of great use to us as TRIGGER_PAUSE_MENU_EVENT will be called any time the user selects a frontend option and perhaps also when the user attempts to scroll a list item, I haven’t been able to verify such yet. Additionally, LAYOUT_CHANGE is called any time that the selected item changes allowing us to know what the user has selected and when they’ve actually selected it.

I’ll get these properly documented when I’ve got time :slight_smile:

That’s all I’ve got for frontend research, let me know if something doesn’t make sense as I basically wrote all of this down over the course of a day. Perhaps an effort could be made to document all of the different components and item implementations for future use…

8 Likes

That menuID-1000 reference is really helpful, as I never quite managed to get why it showed such a wild number on my frontend menu, but on the normal pause menu it had 0, 5, etc IDs. This can be used in a “submenu” type of functionality without having to reload everything.

Now it makes more sense why columnID for setting data is different from the columnID when changing header stats, or adding anything else. Makes me wonder if the players column was switched from the right column to center during development, or maybe some R* dev just didn’t care enough.

This is what I’m currently looking into. When building the menu, navigation through the buttons works, but I didn’t manage to get any “select” event, or input working, so I made my own.
I made a command attached to RegisterKeyMapping(), that uses 0x7E17BE53E1AAABAF to get the uniqueID, and then use that to trigger a specific event.
Works for normal items, but it gets tricky when you add list items in the mix because the uniqueID doesn’t change when the list item changes.

mc… movie clip… ohhh… OOOHHHHH so that’s what it means!

Overall this is a great post and insightful on the ‘behind-the-scenes’. Thank you!

2 Likes

Oh right! I entirely forgot to mention that TRIGGER_PAUSE_MENU_EVENT is invoked when a menu item is selected (what you’re looking for) and LAYOUT_CHANGED corresponds to when the selected item is changed and some other edge cases which are dependant on the loaded page.

Original post is now updated! (Also got the data getter natives the wrong way around so that’s also fixed but as I already pointed out, either data getter works :stuck_out_tongue:)

3 Likes

Thank you very much @Jaymo thats wonderful research!

etc., also explains the map column

4 Likes

Amazing job!! Just one question, is it a pause menu and then opens with esc? because I can’t open it on my server

Actually no, it’s a frontend menu, different than the regular pause menu. I built a small framework around it here if you want to create your own menus.

Menus would have to be opened using a clientside event trigger. You can see everything on the github wiki page.

1 Like

ah ok, thank you!!

1 Like

Mission Completed