Skip to content

Courage.Layout#

require("Courage.Layout") loads a vanilla J2D .blo screen, optionally plays linked animations, and rasterizes the result into the CReact asset registry. Panels can show the result with an image primitive.

Use this when you want to use the original game menus in a mod overlay.

Loading the module#

local Layout = require("Courage.Layout")
import * as Layout from "Courage.Layout"

Active mod context#

Layout.capture and Layout.update require an active mod folder (same rule as Courage.Assets). Call them from init() or other load-time setup, not from deferred callbacks after init finishes.

API#

Function Returns Description
Layout.load(opts) boolean Load a BLO screen. See load options. Unloads any previous layout.
Layout.unload() (none) Tear down the loaded screen, animations, and captured asset.
Layout.getSize() width, height Layout size in pixels, or nil, nil when nothing is loaded.
Layout.capture(rel?, opts?) assetKey, w, h Rasterize the current frame and register RGBA pixels. Default rel is "cache/layout.png". Returns nil, err on failure. See capture options.
Layout.update(rel?) boolean Re-rasterize at the current animation frame and refresh the asset registered by capture.
Layout.setFrame(frame) (none) Jump to an animation frame (applied on the next capture/update).
Layout.advance(delta, loop?) number Advance by delta seconds. loop defaults to true. Returns the new frame.
Layout.getFrameMax() number Max frame across loaded BCK/BPK/BTK clips, or 0 when none are set.
Layout.paneBounds(pane, relativeTo?) x, y, w, h Returns coordinates and dimensions of a pane. Returns nil if not found.
Layout.listPanes() string[] Returns a list of all pane names in the layout.

Capture options#

Layout.capture takes an optional opts table to crop or pad the captured area:

Field Type Default Description
pane string nil Pane name to restrict the capture to. Only the area occupied by this pane is captured.
pad number 0 Extra padding pixels to add around the captured area.
padTop number -1 Top padding (overrides pad).
padRight number -1 Right padding (overrides pad).
padBottom number -1 Bottom padding (overrides pad).
padLeft number -1 Left padding (overrides pad).

Load options#

Layout.load takes one table:

Field Type Default Description
blo string (required) BLO resource name inside the archive (for example "zelda_2d_item_select.blo").
priority number 0x20000 J2D draw priority passed to setPriority.
archive string nil Path to a mod archive when the BLO is not in the main 2D archive.
msgArchive number 0 Message archive index (for example 1 = msgres01) when loading from extracted msg data.
useMain2D boolean false Use dComIfGp_getMain2DArchive() instead of archive / msgArchive.
bck string nil Skeletal animation resource name.
bpk string nil Color pattern animation resource name.
btk string nil Texture SRT animation resource name.
animPane string nil Pane tag to attach BCK/BPK (for example "moya00").
animArchive string nil Archive path for animation files when they are not in the BLO archive.
animMsgArchive number -1 Msg archive index for animation files.
animUseMain2D boolean same as useMain2D Load animation clips from the main 2D archive.

Example#

local Layout = require("Courage.Layout")
local UI = require("Courage.UI")

local MOD_ID = "item-picker"

function ItemPicker:init()
    if not Layout.load({
        blo = "zelda_2d_item_select.blo",
        useMain2D = true,
    }) then
        return
    end

    local key, w, h = Layout.capture("ui/item_select.png")
    if not key then return end

    UI.setState(MOD_ID, { bg = key, w = w, h = h })
    UI.showPopup(MOD_ID)
end

function ItemPicker:shutdown()
    Layout.unload()
    UI.hidePopup()
end

return ItemPicker
import * as Layout from "Courage.Layout"
import * as UI from "Courage.UI"

const MOD_ID = "item-picker"

ItemPicker.init = function () {
    if (!Layout.load({
        blo: "zelda_2d_item_select.blo",
        useMain2D: true,
    })) {
        return
    }

    const [key, w, h] = Layout.capture("ui/item_select.png")
    if (!key) return

    UI.setState(MOD_ID, { bg: key, w, h })
    UI.showPopup(MOD_ID)
}

ItemPicker.shutdown = function () {
    Layout.unload()
    UI.hidePopup()
}

See also#