Patterns
zMenu has three types of patterns, each solving a different reuse problem:
| Type | Folder | Purpose | Referenced via |
|---|---|---|---|
| Inventory Pattern | patterns/ | Reusable decoration / layout buttons shared across inventories | patterns: ["name"] on the inventory |
| Button Pattern | patterns/ | Reusable single-button template with variable substitution | pattern: section inside an item |
| Action Pattern | actions_patterns/ | Default actions applied to every button in an inventory | action-patterns: ["name"] on the inventory |
The first two live in the same folder but are distinguished by the type key in the file.
File Locations
plugins/zMenu/
├── patterns/ # Inventory patterns and button patterns
│ ├── my_decoration.yml # type: INVENTORY (default)
│ └── my_button.yml # type: BUTTON
└── actions_patterns/ # Action patterns
└── default-actions.yml
Subfolders are supported - patterns/borders/simple.yml works.
Inventory Patterns
An inventory pattern defines a set of buttons (decorations, navigation, etc.) that can be applied to any inventory. Think of it as a reusable layer.
Structure
name: "pattern_example" # Unique name used to reference this pattern
size: 54 # Inventory size (must be a multiple of 9)
# type: INVENTORY # Optional - INVENTORY is the default
# enableMultiPage: false # Set to true to allow pagination buttons to work
# matrix: # Optional slot matrix (same syntax as inventories)
items:
example1:
isPermanent: true
item:
material: IRON_INGOT
name: "&fDecoration"
slots:
- 0
- 8
- 45
- 53
| Key | Required | Description |
|---|---|---|
name | Yes | Unique identifier. Inventories reference this name. |
size | No | Defaults to 54. Must be a multiple of 9. |
type | No | INVENTORY (default) or BUTTON. If set to BUTTON the file is loaded as a button pattern instead. |
enableMultiPage | No | When true, NEXT / PREVIOUS buttons inside this pattern will work with the inventory's pagination. Defaults to false. |
matrix | No | Optional slot matrix, same format as in inventories. |
items | Yes | The buttons to place. Same syntax as the items section in an inventory file. |
Using in an Inventory
Reference one or more inventory patterns by name in the patterns list:
name: "&6Shop"
size: 54
patterns:
- "pattern_example"
- "pagination"
items:
# your inventory-specific buttons ...
Patterns are applied in order - buttons from later patterns override earlier ones if they occupy the same slot.
Example: Decoration
File patterns/pattern_example.yml (shipped with zMenu):
name: "pattern_example"
size: 54
items:
example1:
isPermanent: true
item:
material: IRON_INGOT
name: "&fExample of pattern"
slots:
- 0
- 8
- 45
- 53
Usage in an inventory:
name: "&eExample"
size: 54
patterns:
- "pattern_example"
items:
# ...
Example: Navigation with Pagination
File patterns/pagination.yml:
name: "pagination"
size: 54
enableMultiPage: true
items:
next:
type: NEXT
slot: 52
is-permanent: true
item:
material: PAPER
name: "&fNext Page"
previous:
type: PREVIOUS
slot: 49
is-permanent: true
item:
material: PAPER
name: "&fPrevious Page"
The key here is enableMultiPage: true - without it, NEXT and PREVIOUS buttons from the pattern will not function.
Button Patterns
A button pattern is a template for a single button. The inventory that uses it passes variables (%key% placeholders) which are replaced before the button is loaded. This is the most powerful pattern type.
Structure
name: "pattern_cookies" # Unique name
type: BUTTON # Must be BUTTON
button: # Everything under here becomes the button definition
slot: '%slot%'
item:
material: '%material%'
name: "&a%name%"
lore:
- "&7Click to buy"
# click-requirement, actions, view_requirement, else ... all go here
| Key | Required | Description |
|---|---|---|
name | Yes | Unique identifier. |
type | Yes | Must be BUTTON. If omitted the file is treated as an inventory pattern and button: is ignored. |
button | Yes | The button definition. Supports every property a normal button supports (slot, item, actions, click-requirement, view_requirement, else, etc.). |
default-values | No | Fallback values for variables (see below). |
local-placeholders | No | Additional placeholders loaded from the pattern file (see below). |
Using in an Inventory
Inside the items section of an inventory, add a pattern key to the button:
items:
grandma:
pattern:
file-name: "pattern_cookies" # The pattern's name
slot: 20 # Passed as %slot%
material: CAKE # Passed as %material%
name: "Grand Ma" # Passed as %name%
key: "grandma" # Passed as %key%
price: 10 # Passed as %price%
Every key under pattern: (except file-name, plugin-name, and their aliases) becomes a variable. The plugin collects all keys, then replaces %key% placeholders in the pattern's button section before loading.
Variable Substitution
Any value wrapped in %...% inside the pattern file is replaced with the value provided by the inventory. For example, given:
Pattern file:
button:
slot: '%slot%'
item:
material: '%material%'
name: "&a%name%"
Inventory usage:
pattern:
file-name: "my_pattern"
slot: 13
material: DIAMOND
name: "Shiny"
Result: slot 13, material DIAMOND, name &aShiny.
Variable Modifiers
zMenu provides built-in modifiers that transform a variable's value. If a variable is named key, these modifiers are available:
| Modifier | Syntax | Example (key = hello) | Result |
|---|---|---|---|
| Uppercase | %upper_key% | %upper_key% | HELLO |
| Lowercase | %lower_key% | %lower_key% | hello |
| Capitalize | %capitalize_key% | %capitalize_key% | Hello |
| Add one | %add_one_key% | %add_one_key% (key = 5) | 6 |
| Remove one | %remove_one_key% | %remove_one_key% (key = 5) | 4 |
add_one and remove_one parse the value as an integer. If parsing fails, the placeholder is left unchanged.
These modifiers work on any variable - for example if the inventory passes level: 3, then %remove_one_level% resolves to 2.
Special Keys
These keys are not passed as variables - they control how the pattern is loaded:
| Key | Aliases | Description |
|---|---|---|
file-name | fileName, file | Name of the pattern file (without .yml) |
plugin-name | pluginName, plugin | Load the pattern from another plugin's patterns/ folder |
default-values
You can define fallback values directly in the pattern file. If the inventory does not provide a variable, the default is used instead:
name: "my_template"
type: BUTTON
default-values:
material: STONE
name: "Default Name"
slot: 0
button:
slot: '%slot%'
item:
material: '%material%'
name: "%name%"
If the inventory only provides slot: 5, the button will use material STONE and name Default Name.
Priority: Inventory values take precedence over default values.
local-placeholders
Additional placeholders can be defined in the pattern file under local-placeholders. These are loaded after inventory values and default values, so they have the lowest priority among pattern-defined values:
name: "my_template"
type: BUTTON
local-placeholders:
prefix: "&7[Shop] "
button:
item:
name: "%prefix%%name%"
List Variables
When a variable's value is a YAML list, zMenu expands each entry into a separate line in contexts like lore. This is useful for dynamic reward descriptions:
Inventory:
items:
reward:
pattern:
fileName: "playtime_reward"
rewards:
- '&f50$'
- '&fx4 Iron Ingots'
commands:
- 'eco give %player% 50'
- 'give %player% iron_ingot 4'
Pattern file (lore section):
lore:
- '&eRewards:'
- '%rewards%'
- ''
Result: The %rewards% line is expanded into two lines (&f50$ and &fx4 Iron Ingots), so the lore shows:
&eRewards:
&f50$
&fx4 Iron Ingots
The same expansion applies to commands lists used in actions - a %commands% placeholder in a console_command action's commands list will expand into multiple commands.
view-requirement and else at Item Level
You can add view-requirement and else from the inventory side, outside the pattern's button section. These are loaded separately and applied on top of the pattern button:
items:
my_button:
pattern:
file-name: "my_template"
slot: 10
name: "VIP Item"
view-requirement:
requirements:
- type: permission
permission: "vip.access"
else:
item:
material: BARRIER
name: "&cVIP Only"
This lets you reuse the same pattern but add different visibility conditions per inventory without modifying the pattern file.
Note: The pattern file itself can also contain view-requirement and else inside its button section - both sources are supported.
Example: Cookie Clicker
Pattern file patterns/pattern_cookies.yml:
name: "pattern_cookies"
type: BUTTON
button:
click-requirement:
purchase:
clicks:
- ALL
requirements:
- type: placeholder
math: true
placeholder: "%zmenu_player_value_cookie%"
value: "%price%+(%price%*%zmenu_player_value_%key%%*1.15)"
action: SUPERIOR_OR_EQUAL
success:
- type: data
action: SUBTRACT
key: 'cookie'
value: '%price%+(%price%*%zmenu_player_value_%key%%*1.15)'
math: true
- type: data
action: ADD
key: '%key%'
value: '1'
- type: inventory
inventory: 'cookies'
deny:
- type: message
messages:
- "&cYou don't have enough cookies"
slot: '%slot%'
item:
material: '%material%'
name: "&a%name% #353535(&7%zmenu_player_value_%key%%#353535)"
lore:
- ""
- "&fPrice&8: &b%zmenu_formatted_math_%price%+(%price%*{zmenu_player_value_%key%}*1.15)% cookies"
- ""
- "&aClick to buy"
Inventory file inventories/examples/cookies.yml:
name: "&8Cookies"
size: 36
items:
cookie:
slot: 13
item:
material: COOKIE
name: "#77ff77Cookies#353535: &f%zmenu_player_value_cookie%"
lore:
- ""
- "&fCookie per click&8: &b%zmenu_formatted_math_1+({zmenu_player_value_grandma}*2)+({zmenu_player_value_factory}*5)+({zmenu_player_value_mine}*10)%"
- ""
- "&7Click on me !"
actions:
- type: data
action: ADD
key: 'cookie'
value: '1+(%zmenu_player_value_grandma%*2)+(%zmenu_player_value_factory%*5)+(%zmenu_player_value_mine%*10)'
math: true
- type: refresh
grandma:
pattern:
file-name: "pattern_cookies"
plugin-name: "zMenu"
slot: 20
material: CAKE
name: "Grand Ma"
key: "grandma"
price: 10
factory:
pattern:
file-name: "pattern_cookies"
plugin-name: "zMenu"
slot: 22
material: WHEAT
name: "Factory"
key: "factory"
price: 20
mine:
pattern:
file-name: "pattern_cookies"
plugin-name: "zMenu"
slot: 24
material: IRON_PICKAXE
name: "Mine"
key: "mine"
price: 50
The same pattern_cookies template is reused three times with different variables, creating three upgrade buttons from a single pattern.
Example: Playtime Rewards
This example shows nested else chains and the %remove_one_level% modifier. The pattern displays a different item depending on the player's state (already claimed, not enough playtime, must claim previous level, or ready to claim).
Pattern file patterns/playtime_reward.yml (simplified):
name: "playtime_reward"
type: BUTTON
button:
updateOnClick: true
page: '%page%'
slot: '%slot%'
# State 1: Already claimed (level >= current)
view-requirement:
requirements:
- type: placeholder
placeholder: "%zmenu_player_value_playtime_level%"
value: '%level%'
action: SUPERIOR_OR_EQUAL
item:
url: '%head%'
name: '%color%Level %level%'
lore:
- '%rewards%' # List variable - expands into multiple lines
- '&cAlready claimed'
else:
# State 2: Not enough playtime
view-requirement:
requirements:
- type: placeholder
placeholder: "%zmenu_statistic_hours_played%"
value: '%hour%'
action: LOWER
item:
url: '%head%'
name: '%color%Level %level%'
lore:
- '%rewards%'
- '&cNot enough play time'
else:
# State 3: Must claim previous level first
view-requirement:
requirements:
- type: placeholder
placeholder: "%zmenu_player_value_playtime_level%"
value: '%remove_one_level%' # level minus 1
action: LOWER
item:
url: '%head%'
name: '%color%Level %level%'
lore:
- '%rewards%'
- '&cClaim the previous reward first'
else:
# State 4: Ready to claim
click-requirement:
right_click:
clicks:
- ALL
requirements:
- type: placeholder
placeholder: "%zmenu_player_value_playtime_level%"
value: '%remove_one_level%'
action: EQUAL_TO
- type: placeholder
placeholder: "%zmenu_statistic_hours_played%"
value: '%hour%'
action: SUPERIOR_OR_EQUAL
success:
- type: data
action: SET
key: 'playtime_level'
value: '%level%'
- type: console_command
commands:
- '%commands%' # List variable - expands into multiple commands
- type: sound
sound: ENTITY_PLAYER_LEVELUP
item:
url: '%head%'
name: '%color%Level %level%'
lore:
- '%rewards%'
- '&aClick to claim'
Inventory usage (one button shown):
items:
btn-10:
pattern:
fileName: "playtime_reward"
slot: 10
level: 1
hour: 1
color: "#7f7f7f"
head: "eyJ0ZXh0dXJlcyI6..."
rewards:
- '&f50$'
- '&fx4 Iron Ingots'
commands:
- 'eco give %player% 50'
- 'give %player% iron_ingot 4'
Key points:
%remove_one_level%uses theremove_onemodifier on thelevelvariable - iflevelis3, this becomes2%rewards%is a list variable - it expands into two lore lines%commands%is a list variable - it expands into two console commands- The nested
elsechain creates a state machine with four visual states
Action Patterns
Action patterns define default actions that apply to every button in an inventory (unless a button defines its own actions).
Structure
name: default-actions
actions:
- type: sound
sound: ENTITY_VILLAGER_YES
deny-actions:
- type: sound
sound: ENTITY_VILLAGER_NO
| Key | Description |
|---|---|
name | Unique identifier for this action pattern |
actions | Actions executed when a button click succeeds |
deny-actions | Actions executed when a requirement denies a click |
Using in an Inventory
Reference action patterns by name in the action-patterns list:
name: "&6Shop"
size: 54
action-patterns:
- "default-actions"
items:
# ...
Like patterns, the action-patterns list only accepts strings. Objects are not supported.
Example
File actions_patterns/default-actions.yml (shipped with zMenu):
name: default-actions
actions:
- type: sound
sound: ENTITY_VILLAGER_YES
deny-actions:
- type: sound
sound: ENTITY_VILLAGER_NO
With this pattern applied, every button in the inventory will play ENTITY_VILLAGER_YES on a successful click and ENTITY_VILLAGER_NO on a denied click, unless the button overrides these actions.
Variable Resolution Order
When using button patterns, variables are resolved in this order (highest priority first):
- Inventory values - keys defined under
pattern:in the inventory file - default-values - fallback values defined in the pattern file
- Global placeholders - plugin-wide placeholders registered via the API
- local-placeholders - extra placeholders defined in the pattern file
If the same key exists at multiple levels, the highest-priority source wins.
Cross-Plugin Patterns
To load a pattern from another plugin's patterns/ folder, use plugin-name:
items:
my_button:
pattern:
file-name: "shared_template"
plugin-name: "OtherPlugin"
slot: 13
This looks for the file plugins/OtherPlugin/patterns/shared_template.yml.
Accepted aliases: plugin-name, pluginName, plugin.
Synchronizing from the Inventory Builder
Managed button patterns created in the Inventory Builder can be downloaded as YAML or applied directly
to a linked server. The server writes them to plugins/<plugin-name>/patterns/<file-name>.yml, verifies
the downloaded hash, and hot-reloads the pattern.
When an inventory uses one or more managed button patterns, the sync confirmation also lets you install those dependencies before the inventory itself is downloaded and reloaded. This prevents the inventory from being reloaded while one of its pattern files is missing or outdated.
Only saved pattern revisions are synchronized. Link the server with the website commands before using this action in the builder.
Reloading
/zm reload
All patterns (inventory, button, and action) are reloaded when the plugin restarts or when you run the reload command.
Common Mistakes
Objects in the patterns list
# WRONG - will be silently ignored
patterns:
- pattern:
file-name: "my_decoration"
# CORRECT
patterns:
- "my_decoration"
The patterns list uses getStringList() internally and only accepts strings. To use a button pattern with variables, place the pattern: key inside the items section on a specific button - not in the top-level patterns list.
Forgetting type: BUTTON
# WRONG - treated as an inventory pattern, "button:" section is ignored
name: "my_template"
button:
slot: '%slot%'
item:
material: DIAMOND
# CORRECT
name: "my_template"
type: BUTTON
button:
slot: '%slot%'
item:
material: DIAMOND
Without type: BUTTON, the file is loaded as an inventory pattern. Since it has no items section, it will fail to load.
Confusing inventory patterns and button patterns
- Inventory patterns are referenced in the top-level
patterns: ["name"]list. They define a set of buttons. They do not support variable substitution. - Button patterns are referenced via
pattern:inside a single item definition. They define one button with%variable%placeholders.
Expecting variables in inventory patterns
Inventory patterns do not perform variable substitution. If you need %placeholders%, use a button pattern (type: BUTTON) instead.