A shop block (block + menu)
Status: Implemented for a block that opens a chest-style menu its mod controls. Block entities (per-position inventories and progress that save with the world), custom menu layouts and their screens are Planned; this guide keeps the shop’s stock in the mod.
Goal: a Ruby Trader block. Right-click it and a one-row chest menu opens with offers; clicking an offer pays rubies and gives the item. It works for vanilla clients too, because the menu is a vanilla chest.
Language: Lua, with C++ at the end. APIs: Inventories and menus, Blocks and block states.
1. The offers
local OFFERS = {
[0] = { item = "minecraft:diamond", price = 3 },
[1] = { item = "minecraft:golden_apple", price = 5 },
[2] = { item = "minecraft:ender_pearl", price = 2 },
}
local RUBY = "my_mod:ruby"
2. The menu
local function open_trader(player)
local menu = cl.menu.open(player, {
kind = "chest_9x1",
title = "Ruby Trader",
-- locked (the default): every click is cancelled unless on_click returns true
on_click = function(m, who, slot)
local offer = OFFERS[slot]
if not offer then return end
if who:count_item(RUBY) < offer.price then
who:send_action_bar("You need " .. offer.price .. " rubies.")
return
end
who:take(RUBY, offer.price)
who:give(offer.item)
who:send_action_bar("Thanks!")
end,
})
for slot, offer in pairs(OFFERS) do menu:set_item(slot, offer.item) end
end
Count first: take removes whatever it finds, even when it is less than asked.
3. The block
cl.on("lifecycle.register", function()
cl.blocks.register{
id = "ruby_trader",
copy_properties_from = "minecraft:barrel",
vanilla_fallback = "minecraft:barrel",
item = "my_mod:ruby_trader",
behavior = {
use_without_item = function(ctx, player)
open_trader(player)
return "success"
end,
},
}
end)
The behaviour runs on the server, so the menu opens for that player. Add the block’s assets as in A custom block.
4. C++
void open_trader(clight::Entity player) {
auto menu = clight::menu::open(player, {
.kind = CL_MENU_CHEST_9X1,
.title = "Ruby Trader",
.on_click = [](clight::Menu, clight::Entity who, int32_t slot, int32_t, cl_click_type) {
if (slot == 0 && who.count_item("my_mod:ruby") >= 3) {
who.take("my_mod:ruby", 3);
who.give("minecraft:diamond");
}
return false; // keep the menu locked
},
});
if (menu) menu->set_item(0, clight::item("minecraft:diamond"));
}
Checklist
- [ ] The block registers in
lifecycle.registerand has its assets. - [ ] Offers check the price before taking.
- [ ] Slot numbers start at 0 in the menu’s top row.
Planned
A block entity type with its own inventory that saves with the chunk, hoppers moving items in and out, progress bars synced to the menu, and a custom menu screen (RmlUi with slot elements).