global::Menu.Menu is a global::MainLoopProcess with a bounded visible lifetime. It owns its pages, Futile containers, selection state, input packages, and menu sounds. A custom page belongs to one identified menu instance, and its controls belong to that page’s subObjects collection.

flowchart TD
    A[Menu instance] --> B[Menu.pages]
    B --> C[Page]
    C --> D[Page.subObjects]
    D --> E[Controls]

This chain provides update, drawing, selection, and cleanup context. A sprite added directly to Futile.stage sits outside it and will not be found through page cleanup.

Identify the menu before adding a page

Start with the concrete menu that should own the page. Inspect that type’s constructor or page setup method and record four details:

  1. The point where its existing pages are created and added to pages.
  2. The MenuObject owner passed to each global::Menu.Page constructor.
  3. The signal and selection route used to enter, leave, or change the page.
  4. The teardown route, including any state held outside page subObjects.

global::Menu.Page takes (Menu menu, MenuObject owner, string name, int index). Its constructor creates a MouseCursor and adds that cursor to subObjects. To participate in the menu lifecycle, a new page must also be inserted into the target menu’s pages list at that menu’s verified setup point.

existing concrete menu setup
    -> construct Page with that menu's verified owner and index
    -> add the Page to menu.pages
    -> add controls to page.subObjects
    -> connect the menu's existing signal and selection paths

This is anatomy rather than a complete implementation. The inspected base types do not supply one public hook that safely adds a page to every menu. They also do not establish a general registration method for a new primary menu process. Copying another menu’s owner, page index, or signal string without checking its concrete class can leave the page unreachable or send selection to the wrong object.

Keep objects with their page

Menu.GrafUpdate(float) calls GrafUpdate(timeStacker) on every page. Page and control state should therefore stay attached to the menu objects that receive those calls. Put custom labels, buttons, and other menu objects in the page’s subObjects collection through the same construction pattern used by the target menu.

The menu owns a main container and a cursorContainer. Container placement still depends on the concrete object’s drawing code. Ownership and drawing layer are related, but adding a node to a container does not add it to pages or subObjects.

Input also belongs to the target menu. The base menu stores input packages and selection state, but static inspection of Menu and Page does not define one controller and mouse route for all subclasses. Trace the chosen menu’s button signals, selected object changes, page transitions, and back action before publishing a complete interaction example.

Let shutdown remove the page

Menu.ShutDownProcess() calls RemoveSprites() on each page. It then removes the menu containers and info label and clears menu sound loops that are not background loops. A page feature should make its visual resources reachable through the normal MenuObject removal path. Any subscription or resource held outside that tree needs cleanup tied to the same menu process.

Use Remix settings when the goal is a mod configuration page. Remix already owns registration, tabs, controls, and persistence. Use HUD parts for an overlay shown during gameplay. Those owners have different creation and cleanup paths from a primary menu process.

No custom page was opened for this page. Before publishing code for a concrete menu, test mouse and controller selection, page entry and exit, repeated menu construction, process switching, and sprite removal. The source verifies ownership and cleanup structure only.

Sources

  • Rain World v1.11.8, Steam app 312520, build 22785462. Assembly-CSharp.dll SHA-256 B6BE1D4E18CE219D21091B51564CB6A11C1E4106B41DE903EB8E58849CB16FDB.
  • Assembly-CSharp.dll: global::Menu.Menu, global::Menu.Menu.GrafUpdate(System.Single), and global::Menu.Menu.ShutDownProcess().
  • Assembly-CSharp.dll: global::Menu.Page..ctor(global::Menu.Menu,global::Menu.MenuObject,System.String,System.Int32) and global::Menu.MenuObject.RemoveSprites().
  • Processes and sessions explains the process lifetime surrounding a menu.
  • Runtime reference records the same installed assembly baseline.