Skip to Content
Modlock

Interfaces

Draw a HUD, a menu or a notice on a player's screen.

Each language has an interface library: JSX in TypeScript, @modlock/ui in Luau, modlock.ui in Python and mod/ui in Go. Each library has a HUD kit (hud) and style tokens (theme). This screen asks the player to click. The mod frees the cursor and blocks the buttons a click would fire, and closePicks releases them:

import { Buttons, freeCursor, hide, type Player, show } from 'modlock'

function openPicks(player: Player): void {
  show(
    player,
    <panel style={{ flow: 'down', padding: 24, background: '#101820e0' }}>
      <label style={{ fontSize: 28 }}>Pick a hero</label>
      <button onPress={closePicks}>Ready</button>
    </panel>,
  )
  freeCursor(player, true)
  player.blockInput(Buttons.attack | Buttons.altFire | Buttons.melee)
}

function closePicks(player: Player): void {
  player.blockInput(0n)
  hide(player)
}
local modlock = require("@modlock")
local ui = require("@modlock/ui")

local function closePicks(player: modlock.Player)
	player:blockInput(0)
	ui.hide(player)
end

local function openPicks(player: modlock.Player)
	ui.show(player, ui.panel {
		style = { flow = "down", padding = 24, background = "#101820e0" },
		ui.label { style = { fontSize = 28 }, "Pick a hero" },
		ui.button { onPress = closePicks, "Ready" },
	})
	ui.freeCursor(player, true)
	-- Each button is a bit, so their sum blocks all of them.
	player:blockInput(modlock.Buttons.attack + modlock.Buttons.altFire + modlock.Buttons.melee)
end
import modlock
from modlock.ui import button, free_cursor, hide, label, panel, show


def open_picks(player: modlock.Player) -> None:
    show(player, panel(style={"flow": "down", "padding": 24, "background": "#101820e0"})[
        label(style={"font_size": 28})["Pick a hero"],
        button(on_press=close_picks)["Ready"],
    ])
    free_cursor(player, True)
    player.block_input(modlock.Buttons.ATTACK | modlock.Buttons.ALT_FIRE | modlock.Buttons.MELEE)


def close_picks(player: modlock.Player) -> None:
    player.block_input(0)
    hide(player)
// import "github.com/paralin/modlock/mod/ui"
func openPicks(p mod.Player) {
	ui.Show(p, ui.Panel{
		Style: ui.Style{Flow: ui.Down, Padding: ui.Edges{24}, Background: "#101820e0"},
		Children: ui.Nodes{
			ui.Label{Style: ui.Style{FontSize: 28}, Text: "Pick a hero"},
			ui.Button{OnPress: closePicks, Children: ui.Text("Ready")},
		},
	})
	ui.FreeCursor(p, true)
	_ = p.BlockInput(mod.ButtonAttack | mod.ButtonAltFire | mod.ButtonMelee)
}

func closePicks(p mod.Player) {
	_ = p.BlockInput(0)
	ui.Hide(p)
}

Calls

Luau spells each call as TypeScript does, Python in snake_case, and Go capitalizes it. In Go, toast with seconds is ToastFor, and uiSound is Sound.

CallEffect
show(player, element), hide(player)Show a player an interface written in JSX, or remove it.
toast(player, text, seconds?)Show a player a short notice that fades.
uiSound(player, sound, { seconds? })Play a game sound event to one player once, hero or not; seconds stops it early.
hideHud(player, parts)Hide parts of the game's HUD, such as 'topBar' or 'minimap'; [] shows them again.
freeCursor(player, free)Show the mouse cursor without Tab, so the player can click; false gives the mouse back to aiming.
abilityBarPresses(player, onPress?)Take clicks on the game's ability bar by slot; see Abilities.
abilityDisplay(viewer, rows?)Show players' replaced abilities on Tab and the ability bar; no rows brings back the game's own.
cursorFree(player)Read whether the player's cursor is up now, however it got there.
items, item(name)The shop's items: id, slot, tier, active, imbue and icon. TypeScript only.
player.screenEffect(effect, timing?)Play 'black', 'killed', 'blinded', 'darkness' or 'matchIntro' over the view.
player.clearScreenEffect(effect)End that effect, such as lifting a black screen at a round's start.
route(player, name, value)Hand every layout of the mod value under name; see Layouts.

The cursor

While the cursor is up, the game still shoots, turns and moves the hero. The mod blocks those buttons itself, and releases them in the same call that hides the screen. hide gives the mouse back to aiming.

The player can also put the cursor away with the game's own keys. The cursor then stays away while the screen changes, until the next freeCursor(player, true) shows it again. cursorFree(player) reports whether the cursor is up, and nothing while the player has no hero. A mod that wants the cursor back calls freeCursor again when cursorFree reports false, on a press or a timer.

Style it

Style panel, label, image, hero, button and layout with:

GroupProperties
Layoutwidth, height, minWidth, maxWidth, minHeight, maxHeight, x, y, zIndex, margin, padding, horizontalAlign, verticalAlign, flow ('down', 'right', 'downWrap', 'rightWrap'), overflow, overflowX, overflowY ('squish', 'clip', 'scroll', 'show')
Paintbackground, gradient ({ shape, colors, at }), borderRadius, borderWidth, borderColor, boxShadow, opacity, tint, saturation, brightness, contrast, blur
Textcolor, fontSize, face (the game's body, block or serif), bold, weight, italic, uppercase, letterSpacing, lineHeight, textAlign, noWrap, textOverflow ('ellipsis', 'shrink', 'clip'), decoration ('underline', 'strike'), shadow, textShadow
Motiontranslate, scale, rotate, origin, transition (seconds or { seconds, delay, easing }); a change to a property the game animates eases instead of jumping

Elements and props

Prop or elementOnDoes
hoveranyA style laid over the element's own while the pointer is on it.
tooltipanyText the game shows beside it.
itemanyAn item's class name. The game shows the item's shop card in the element's place.
abilityany{ name, player }. The game shows its window for that ability in the element's place, in the player's language with the numbers of player's hero.
hoverSoundanyA game sound event, such as 'UI.MainMenu.Hover'.
enter, exitanyStyles it eases from as it appears and to before it goes, with its transition.
cycleany{ style, seconds, count }: swings between its style and this one, count times there and back, or forever.
sound, disabledbuttonA sound played on a press; disabled stops presses.
scalingimage'fit', 'cover', 'stretch', 'fitWidth', 'fitHeight', 'none'.
<hero hero={15} look>The game's picture of a hero: small, card, icon, vertical, topBar or minimap.
token, variableslabelThe game's own text, such as #Citadel_HeroName_Haze, in the player's language.
<avatar steamId>A player's Steam picture, from an entry of players().

An interface cannot load pages, styles or scripts from outside the game.

Inputs

Each input hands each change to onChange(player, value):

InputPropsValue
<entry>value, placeholder, maxLengthText, cut to maxLength characters.
<slider>value, min, max, stepA number, clamped to the range and step.
<toggle>on, label as childrenOn or off.
<dropdown>options, selectedThe chosen option's index.

The host drops a change to a disabled input or one the player no longer sees. The player's edit shows until your mod draws a new value, so store the value your handler receives and draw it back.

The HUD kit

The HUD kit draws components in the look of the game's own HUD. This sample draws a round's score across the top of the screen:

import { Score, show, TopStrip } from 'modlock'

show(
  player,
  <TopStrip heading="Round 2">
    <Score points={[1, 0]} />
  </TopStrip>,
)
local hud = require("@modlock/hud")

ui.show(player, hud.TopStrip { heading = "Round 2", hud.Score { points = { 1, 0 } } })
from modlock.hud import score, top_strip

show(player, top_strip(heading="Round 2")[score(points=(1, 0))])
// import "github.com/paralin/modlock/mod/hud"
ui.Show(p, hud.TopStrip{Heading: "Round 2", Children: hud.Score{Points: [2]int{1, 0}}})
ComponentDraws
<Text role>Text in one of the kit's type roles.
<Plate>, <Pill>A dark plate under text, so the text reads over a bright sky.
<Pips>A count, as the round counter shows it.
<Tag>A status label.
<Banner>An announcement of a moment.
<TopStrip>, <Score>The top of the screen, with a score in it.
<Roster>The players down the top right corner, with hero portraits.
<Action kind>A button in the game's menu look, with its hover, sounds and disabled state.
<Meter value>A bar that eases to each new value.

theme contains the tokens for a mod's own panels, such as theme.tones and theme.sizes, and each entry in heroes has its small portrait. Layouts covers layouts from the mod's content, style sheets and how the game draws an interface.

On this page