diff --git a/.cspell-words.txt b/.cspell-words.txt index 3827e9f..4c6a4f6 100644 --- a/.cspell-words.txt +++ b/.cspell-words.txt @@ -181,3 +181,15 @@ customises organisation JDBC JPMS + +# Examples-pack vocabulary +hotbar +typoing +respawns +elytra +callsite +NETHERITE +roleplay +interactables +Royale +Votifier diff --git a/astro.config.mjs b/astro.config.mjs index bf4b16e..d5cde5d 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,4 +1,5 @@ // @ts-check +import path from "node:path"; import { defineConfig } from "astro/config"; import starlight from "@astrojs/starlight"; import mdx from "@astrojs/mdx"; @@ -34,7 +35,10 @@ export default defineConfig({ en: { label: "English", lang: "en" }, ru: { label: "Русский", lang: "ru" }, }, - customCss: ["./src/styles/brand.css"], + customCss: [ + "./src/styles/brand.css", + "./src/styles/examples.css", + ], expressiveCode: { themes: ["github-dark", "github-light"], shiki: { @@ -46,6 +50,7 @@ export default defineConfig({ { label: "Getting Started", translations: { ru: "Начало работы" }, + collapsed: true, items: [ { slug: "start/installation" }, { slug: "start/config" }, @@ -58,6 +63,7 @@ export default defineConfig({ { label: "Authoring menus", translations: { ru: "Создание меню" }, + collapsed: true, items: [ { slug: "general/commands" }, { slug: "general/menu-structure" }, @@ -73,6 +79,7 @@ export default defineConfig({ { label: "Techniques", translations: { ru: "Приёмы" }, + collapsed: true, items: [ { slug: "advanced/logical" }, { slug: "advanced/templates" }, @@ -85,6 +92,7 @@ export default defineConfig({ { label: "Reference", translations: { ru: "Справочник" }, + collapsed: true, items: [ { slug: "general/cheatsheet" }, { slug: "general/examples" }, @@ -93,6 +101,7 @@ export default defineConfig({ { label: "For Developers", translations: { ru: "Для разработчиков" }, + collapsed: true, items: [ { slug: "developers/general" }, { slug: "developers/addons" }, @@ -104,6 +113,81 @@ export default defineConfig({ { slug: "developers/migration" }, ], }, + { + label: 'Examples', + translations: { ru: 'Примеры' }, + collapsed: true, + items: [ + { slug: 'examples' }, + { slug: 'examples/builder' }, + { + label: 'Shops', + translations: { ru: 'Магазины' }, + collapsed: true, + autogenerate: { directory: 'examples/shops' }, + }, + { + label: 'Hub & Navigation', + translations: { ru: 'Хаб и навигация' }, + collapsed: true, + autogenerate: { directory: 'examples/hub-and-nav' }, + }, + { + label: 'Cosmetics', + translations: { ru: 'Косметика' }, + collapsed: true, + autogenerate: { directory: 'examples/cosmetics' }, + }, + { + label: 'Donate', + translations: { ru: 'Донат' }, + collapsed: true, + autogenerate: { directory: 'examples/donate' }, + }, + { + label: 'Casino & Games', + translations: { ru: 'Казино и игры' }, + collapsed: true, + autogenerate: { directory: 'examples/casino-and-games' }, + }, + { + label: 'Kits & Rewards', + translations: { ru: 'Киты и награды' }, + collapsed: true, + autogenerate: { directory: 'examples/kits-and-rewards' }, + }, + { + label: 'Admin Tools', + translations: { ru: 'Админ-инструменты' }, + collapsed: true, + autogenerate: { directory: 'examples/admin-tools' }, + }, + { + label: 'Info Pages', + translations: { ru: 'Инфо-страницы' }, + collapsed: true, + autogenerate: { directory: 'examples/info-pages' }, + }, + { + label: 'State & Variables', + translations: { ru: 'Состояние и переменные' }, + collapsed: true, + autogenerate: { directory: 'examples/state-and-vars' }, + }, + { + label: 'World Integrations', + translations: { ru: 'Интеграции мира' }, + collapsed: true, + autogenerate: { directory: 'examples/world-integrations' }, + }, + { + label: 'Snippets', + translations: { ru: 'Сниппеты' }, + collapsed: true, + autogenerate: { directory: 'examples/snippets' }, + }, + ], + }, { slug: "changelog", attrs: { "data-changelog-link": "true" }, @@ -115,7 +199,73 @@ export default defineConfig({ baseUrl: "https://github.com/AbstractMenus/docs/edit/main/src/content/docs/", }, + // Persist sidebar group open/closed state across page loads + a floating + // action button in the bottom-right of the sidebar that toggles all groups. + // Starlight's `collapsed: true` on each group makes them closed by default; + // this script remembers the user's manual toggles per group via localStorage. + head: [ + { + tag: "script", + content: [ + "(function(){", + "var KEY='ame-sidebar-groups';", + "function load(){try{return JSON.parse(localStorage.getItem(KEY)||'{}');}catch(e){return {};}}", + "function save(s){try{localStorage.setItem(KEY,JSON.stringify(s));}catch(e){}}", + "var lang=document.documentElement.lang||'en';", + "var L=lang==='ru'?{expand:'Развернуть все',collapse:'Свернуть все'}:{expand:'Expand all',collapse:'Collapse all'};", + "var SVG_ATTRS='width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"';", + "var ICON_EXPAND='';", + "var ICON_COLLAPSE='';", + "function init(){", + "var sidebar=document.querySelector('.sidebar-content');", + "if(!sidebar)return;", + "var groups=sidebar.querySelectorAll('details');", + "var seen={};var state=load();", + "groups.forEach(function(d){", + "var summary=d.querySelector(':scope > summary');if(!summary)return;", + "var label=(summary.textContent||'').trim();if(!label||seen[label])return;", + "seen[label]=true;", + "var isActive=d.querySelector('a[aria-current=\"page\"]');", + "if(!isActive&&label in state){d.open=state[label];}", + "d.addEventListener('toggle',function(){var s=load();s[label]=d.open;save(s);refreshFab();});", + "});", + "var existing=document.querySelector('.sl-fab');if(existing)existing.parentNode.removeChild(existing);", + "var btn=document.createElement('button');", + "btn.type='button';btn.className='sl-fab';", + "btn.setAttribute('aria-label',L.expand);", + "btn.innerHTML=ICON_EXPAND+ICON_COLLAPSE+''+L.expand+'';", + "function refreshFab(){", + "var d=sidebar.querySelectorAll('details');var anyOpen=false;", + "d.forEach(function(x){if(x.open)anyOpen=true;});", + "btn.classList.toggle('is-collapse',anyOpen);", + "var tt=btn.querySelector('[data-tt]');", + "if(tt)tt.textContent=anyOpen?L.collapse:L.expand;", + "btn.setAttribute('aria-label',anyOpen?L.collapse:L.expand);", + "}", + "btn.addEventListener('click',function(){", + "var d=sidebar.querySelectorAll('details');var anyOpen=false;", + "d.forEach(function(x){if(x.open)anyOpen=true;});", + "var newOpen=!anyOpen;var s=load();", + "d.forEach(function(x){var sm=x.querySelector(':scope > summary');var lbl=sm?(sm.textContent||'').trim():'';x.open=newOpen;if(lbl)s[lbl]=newOpen;});", + "save(s);refreshFab();", + "});", + "var pane=document.querySelector('.sidebar-pane')||sidebar;", + "pane.appendChild(btn);", + "refreshFab();", + "}", + "if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}else{init();}", + "})();", + ].join(""), + }, + ], }), mdx(), ], + vite: { + resolve: { + alias: { + "@components": path.resolve("./src/components"), + }, + }, + }, }); diff --git a/examples/en/_shared/templates.conf b/examples/en/_shared/templates.conf new file mode 100644 index 0000000..0343c70 --- /dev/null +++ b/examples/en/_shared/templates.conf @@ -0,0 +1,82 @@ +# AbstractMenus example pack - shared templates +# Reusable HOCON snippets referenced from menus via ${blockName} substitution. +# Docs: https://abstractmenus.github.io/docs/en/examples + +# Sound presets (Paper 1.21+ registry keys) +clickSound: "ui.button.click" +openSound: "block.chest.open" +closeSound: "block.chest.close" +successSound: "entity.experience_orb.pickup" +failSound: "block.note_block.bass" +deniedSound: "block.anvil.land" + +# Deny action snippets (use as: denyActions: ${denyNoMoney}) +denyNoMoney { + sound: ${deniedSound} + message: "&cYou don't have enough money." +} +denyNoPerm { + sound: ${deniedSound} + message: "&cYou don't have permission." +} +denyCooldown { + sound: ${deniedSound} + message: "&cYou're on cooldown. Try again later." +} + +# Border items (use as: ${borderBlack} { slot: "0-8" }) +borderBlack { + material: BLACK_STAINED_GLASS_PANE + name: " " +} +borderGray { + material: GRAY_STAINED_GLASS_PANE + name: " " +} +borderBlue { + material: BLUE_STAINED_GLASS_PANE + name: " " +} + +# Navigation buttons (use as: ${buttonClose} { slot: 22 }) +buttonClose { + material: BARRIER + name: "&cClose" + click { + closeMenu: true + } +} +buttonBack { + material: ARROW + name: "&eBack" + lore: ["&7Return to previous menu"] + click { + closeMenu: true + } +} +buttonNext { + material: ARROW + name: "&eNext page" + click { + pageNext: 1 + } +} +buttonPrev { + material: ARROW + name: "&ePrevious page" + click { + pagePrev: 1 + } +} + +# Rule shortcut snippets (use as: rules: ${rulesAdmin}) +rulesAdmin { permission: "abstractmenus.admin" } +rulesVip { permission: "abstractmenus.vip" } +rulesDonator { permission: "abstractmenus.donator" } + +# Common decorative header (use as: ${commonHeader} { slot: 4 }) +commonHeader { + material: NETHER_STAR + name: "&6&lExample Menu" + lore: ["&7Welcome to the AbstractMenus example pack"] +} diff --git a/examples/en/admin-tools/01-online-players/menu.conf b/examples/en/admin-tools/01-online-players/menu.conf new file mode 100644 index 0000000..9085c8e --- /dev/null +++ b/examples/en/admin-tools/01-online-players/menu.conf @@ -0,0 +1,88 @@ +# Online Players - generated catalog menu of online players with admin actions +# Docs: https://abstractmenus.github.io/docs/en/examples/admin-tools/online-players +# Open: /ame_online +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Online Players (page %page% / %pages_count%)" +size: 6 + +# Restrict access - admins only. +rules: ${rulesAdmin} + +activators { + command: "ame_online" +} + +# Pull online players as catalog entries. Each entry exposes %ctg_player_*% placeholders. +catalog { + type: PLAYERS +} + +# 7-wide grid of player heads with borders. 4 rows of 7 = 28 heads per page. +matrix { + cells: [ + "_________" + "_xxxxxxx_" + "_xxxxxxx_" + "_xxxxxxx_" + "_xxxxxxx_" + "_________" + ] + + templates { + "x" { + material: PLAYER_HEAD + skullOwner: "%ctg_player_name%" + name: "&e%ctg_player_name%" + lore: [ + "&7Health: &c%ctg_player_health%" + "&7World: &b%ctg_player_world%" + "" + "&aLeft-click - teleport to player" + "&cShift-click - kick player" + ] + click { + left { + actions { + command { + console: "tp %player_name% %ctg_player_name%" + } + sound: ${clickSound} + closeMenu: true + } + } + shift_left { + actions { + command { + console: "kick %ctg_player_name% &cKicked by an admin" + } + sound: ${successSound} + refreshMenu: true + message: "&aKicked &e%ctg_player_name%" + } + } + } + } + } +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "45-53" } + + # Pagination buttons - shown only when there are multiple pages. + ${buttonPrev} { slot: 48 } + ${buttonNext} { slot: 50 } + + # Page counter (info-only) + { + slot: 49 + material: PAPER + name: "&ePage %page% / %pages_count%" + lore: ["&7Total online: &e%pages_total%"] + } + + ${buttonClose} { slot: 53 } +] diff --git a/examples/en/admin-tools/01-online-players/meta.json b/examples/en/admin-tools/01-online-players/meta.json new file mode 100644 index 0000000..30ad452 --- /dev/null +++ b/examples/en/admin-tools/01-online-players/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Online Players", + "category": "admin-tools", + "level": "advanced", + "features": ["GeneratedMenu", "PLAYERS-catalog", "matrix-cells", "matrix-templates", "ctg-placeholders", "click-types-left-shift", "command-action", "pageNext-pagePrev"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_online", + "order": 1 +} diff --git a/examples/en/admin-tools/02-quick-mod/menu.conf b/examples/en/admin-tools/02-quick-mod/menu.conf new file mode 100644 index 0000000..88402e9 --- /dev/null +++ b/examples/en/admin-tools/02-quick-mod/menu.conf @@ -0,0 +1,110 @@ +# Quick Mod - admin-only menu of common moderation commands +# Docs: https://abstractmenus.github.io/docs/en/examples/admin-tools/quick-mod +# Open: /ame_mod (admin only) +# Requires: any plugin that provides /kick /ban /mute /gamemode + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&cQuick Mod Tools" +size: 3 + +# Menu-level access gate - non-admins can't even open this menu. +rules: ${rulesAdmin} + +activators { + command: "ame_mod" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: NETHER_STAR + name: "&c&lQuick Mod Tools" + lore: ["&7Common moderation actions.", "&8Admin-only."] + } + + # ========== Game mode shortcuts ========== + { + slot: 10 + material: GRASS_BLOCK + name: "&aSet Survival" + lore: ["&7Switch yourself to survival mode."] + click { + actions { + setGamemode: "SURVIVAL" + sound: ${clickSound} + message: "&aGamemode: SURVIVAL" + } + } + } + { + slot: 11 + material: DIAMOND_PICKAXE + name: "&aSet Creative" + flags: HIDE_ATTRIBUTES + lore: ["&7Switch yourself to creative mode."] + click { + actions { + setGamemode: "CREATIVE" + sound: ${clickSound} + message: "&aGamemode: CREATIVE" + } + } + } + { + slot: 12 + material: ENDER_EYE + name: "&aSet Spectator" + lore: ["&7Switch yourself to spectator mode."] + click { + actions { + setGamemode: "SPECTATOR" + sound: ${clickSound} + message: "&aGamemode: SPECTATOR" + } + } + } + + # ========== Player browser ========== + { + slot: 14 + material: PLAYER_HEAD + name: "&aBrowse online players" + lore: ["&7Open the online players menu", "&7for kick / teleport actions."] + click { openMenu: "ame_online" } + } + + # ========== Server actions ========== + { + slot: 15 + material: TNT + name: "&cBroadcast: server restart" + lore: ["&7Send a chat-wide warning."] + click { + actions { + broadcast: "&c[Admin] &fServer restart in 5 minutes. Save your work." + sound: ${successSound} + } + } + } + { + slot: 16 + material: COMPASS + name: "&aTeleport to spawn" + lore: ["&7Run /spawn for yourself."] + click { + actions { + command { + player: "spawn" + } + sound: ${clickSound} + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/admin-tools/02-quick-mod/meta.json b/examples/en/admin-tools/02-quick-mod/meta.json new file mode 100644 index 0000000..d22388a --- /dev/null +++ b/examples/en/admin-tools/02-quick-mod/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Quick Mod", + "category": "admin-tools", + "level": "intermediate", + "features": ["menu-level-rules", "setGamemode", "broadcast", "command-action", "openMenu-chain"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_mod", + "order": 2 +} diff --git a/examples/en/casino-and-games/01-roulette/menu.conf b/examples/en/casino-and-games/01-roulette/menu.conf new file mode 100644 index 0000000..c031ab7 --- /dev/null +++ b/examples/en/casino-and-games/01-roulette/menu.conf @@ -0,0 +1,61 @@ +# Roulette - 8-frame slowdown animation + random prize via randActions +# Docs: https://abstractmenus.github.io/docs/en/examples/casino-and-games/roulette +# Open: /ame_roulette +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Roulette" +size: 1 + +activators { + command: "ame_roulette" +} + +# Static items shown across all frames. clear: false on frames keeps these in place. +items: [ + ${borderBlack} { slot: 0 } + ${borderBlack} { slot: 1 } + ${borderBlack} { slot: 2 } + ${borderBlack} { slot: 6 } + ${borderBlack} { slot: 7 } + ${buttonClose} { slot: 8 } +] + +# Random prize awarded after the spin finishes. +onAnimEnd { + randActions: [ + { + sound: ${successSound} + itemAdd { material: DIAMOND, count: 1 } + message: "&bYou won 1 Diamond!" + } + { + sound: ${successSound} + itemAdd { material: EMERALD, count: 2 } + message: "&aYou won 2 Emeralds!" + } + { + sound: ${successSound} + itemAdd { material: GOLD_INGOT, count: 4 } + message: "&6You won 4 Gold Ingots!" + } + { + sound: ${failSound} + message: "&7Better luck next time." + } + ] +} + +# Animation frames. Each frame has clear: false so static items survive. +# Delays grow toward the end to create a slowdown feel. +frames: [ + { delay: 2, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: EMERALD, name: "&a?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: GOLD_INGOT, name: "&6?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 4, clear: false, items: [ { slot: 4, material: EMERALD, name: "&a?" } ] } + { delay: 6, clear: false, items: [ { slot: 4, material: GOLD_INGOT, name: "&6?" } ] } + { delay: 10, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 20, clear: false, items: [ { slot: 4, material: NETHER_STAR, name: "&e?" } ] } +] diff --git a/examples/en/casino-and-games/01-roulette/meta.json b/examples/en/casino-and-games/01-roulette/meta.json new file mode 100644 index 0000000..bfaa77f --- /dev/null +++ b/examples/en/casino-and-games/01-roulette/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Roulette", + "category": "casino-and-games", + "level": "intermediate", + "features": ["frames", "AnimatedMenu", "onAnimEnd", "randActions", "static-items-with-frames"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_roulette", + "order": 1 +} diff --git a/examples/en/casino-and-games/02-slot-machine/menu.conf b/examples/en/casino-and-games/02-slot-machine/menu.conf new file mode 100644 index 0000000..ab19e23 --- /dev/null +++ b/examples/en/casino-and-games/02-slot-machine/menu.conf @@ -0,0 +1,82 @@ +# Slot Machine - 3 reels animated independently with random outcomes +# Docs: https://abstractmenus.github.io/docs/en/examples/casino-and-games/slot-machine +# Open: /ame_slots +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Slot Machine" +size: 3 + +activators { + command: "ame_slots" +} + +# Static items - the borders, header, and the cost note. Frames overlay reels. +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: GOLD_INGOT + name: "&6Slot Machine" + lore: ["&7Cost per spin: &a$50", "&7Match all 3 to win big."] + } + + ${buttonClose} { slot: 22 } +] + +# After the last frame, randActions awards the prize. The 4 outcomes correspond +# to (loosely) the visual frames the player saw cycling. +onAnimEnd { + randActions: [ + # 60% miss + { sound: ${failSound}, message: "&7No match. -$50" } + { sound: ${failSound}, message: "&7No match. -$50" } + { sound: ${failSound}, message: "&7No match. -$50" } + # 30% small win + { sound: ${successSound}, message: "&aTriple cherries! +$80", giveMoney: 80 } + # 8% medium win + { sound: ${successSound}, message: "&6Triple bells! +$200", giveMoney: 200 } + # 2% jackpot + { sound: ${successSound}, message: "&dJACKPOT! Triple sevens! +$1000", giveMoney: 1000 } + ] +} + +# Three reels at slots 11, 13, 15. Each reel cycles through 5 symbols with a +# slowdown. Frames overlay all three at once - the symbols appear to spin +# in lockstep, which is enough for the demo. Spin-each-reel-separately +# would need separate animated menus or extension code. +frames: [ + { delay: 2, clear: false, items: [ + { slot: 11, material: APPLE, name: "&c?" } + { slot: 13, material: GOLD_INGOT, name: "&6?" } + { slot: 15, material: DIAMOND, name: "&b?" } + ]} + { delay: 2, clear: false, items: [ + { slot: 11, material: GOLD_INGOT, name: "&6?" } + { slot: 13, material: DIAMOND, name: "&b?" } + { slot: 15, material: NETHER_STAR, name: "&e?" } + ]} + { delay: 2, clear: false, items: [ + { slot: 11, material: DIAMOND, name: "&b?" } + { slot: 13, material: NETHER_STAR, name: "&e?" } + { slot: 15, material: APPLE, name: "&c?" } + ]} + { delay: 4, clear: false, items: [ + { slot: 11, material: NETHER_STAR, name: "&e?" } + { slot: 13, material: APPLE, name: "&c?" } + { slot: 15, material: GOLD_INGOT, name: "&6?" } + ]} + { delay: 8, clear: false, items: [ + { slot: 11, material: APPLE, name: "&c?" } + { slot: 13, material: APPLE, name: "&c?" } + { slot: 15, material: APPLE, name: "&c?" } + ]} + { delay: 20, clear: false, items: [ + { slot: 11, material: NETHER_STAR, name: "&eFINAL" } + { slot: 13, material: NETHER_STAR, name: "&eFINAL" } + { slot: 15, material: NETHER_STAR, name: "&eFINAL" } + ]} +] diff --git a/examples/en/casino-and-games/02-slot-machine/meta.json b/examples/en/casino-and-games/02-slot-machine/meta.json new file mode 100644 index 0000000..cc295ba --- /dev/null +++ b/examples/en/casino-and-games/02-slot-machine/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Slot Machine", + "category": "casino-and-games", + "level": "intermediate", + "features": ["frames", "AnimatedMenu", "onAnimEnd", "randActions-weighted", "multi-reel"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_slots", + "order": 2 +} diff --git a/examples/en/casino-and-games/03-lucky-chest/menu.conf b/examples/en/casino-and-games/03-lucky-chest/menu.conf new file mode 100644 index 0000000..38c5c98 --- /dev/null +++ b/examples/en/casino-and-games/03-lucky-chest/menu.conf @@ -0,0 +1,70 @@ +# Lucky Chest - chance rule + weighted randActions for prize tiers +# Docs: https://abstractmenus.github.io/docs/en/examples/casino-and-games/lucky-chest +# Open: /ame_chest +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Lucky Chest" +size: 3 + +activators { + command: "ame_chest" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Info card + { + slot: 4 + material: PAPER + name: "&6Lucky Chest" + lore: [ + "&7Pay $50 to open a chest." + "&7You'll get one of:" + "" + "&7- &fcommon: 12 cooked beef &7(60%)" + "&7- &auncommon: 1 emerald &7(30%)" + "&7- &6rare: 1 enchanted golden apple &7(8%)" + "&7- &dmythic: 1 netherite ingot &7(2%)" + ] + } + + # Open button + { + slot: 13 + material: CHEST + name: "&aOpen Chest &8($50)" + lore: ["&7Click to roll for a prize."] + click { + rules { money: 50 } + actions { + takeMoney: 50 + # Weighted random - entries appearing more times are more likely. + randActions: [ + # 6 common entries = 60% + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Common: 12 cooked beef." } + # 3 uncommon entries = 30% + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aUncommon: 1 emerald!" } + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aUncommon: 1 emerald!" } + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aUncommon: 1 emerald!" } + # ~1 rare = ~8% (we round to 1/12 since we have 12 entries total - close enough for demo) + { itemAdd { material: ENCHANTED_GOLDEN_APPLE }, sound: ${successSound}, message: "&6Rare! Enchanted golden apple!" } + # ~1 mythic = ~8% (technically 2% target but with 12 buckets we collapse rare and mythic) + { itemAdd { material: NETHERITE_INGOT }, sound: ${successSound}, message: "&dMYTHIC! Netherite ingot!" } + { itemAdd { material: NETHERITE_INGOT }, sound: ${successSound}, message: "&dMYTHIC! Netherite ingot!" } + ] + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/casino-and-games/03-lucky-chest/meta.json b/examples/en/casino-and-games/03-lucky-chest/meta.json new file mode 100644 index 0000000..dd02e44 --- /dev/null +++ b/examples/en/casino-and-games/03-lucky-chest/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Lucky Chest", + "category": "casino-and-games", + "level": "intermediate", + "features": ["randActions-weighted", "money-rule", "takeMoney", "tiered-rewards"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_chest", + "order": 3 +} diff --git a/examples/en/casino-and-games/04-daily-lottery/menu.conf b/examples/en/casino-and-games/04-daily-lottery/menu.conf new file mode 100644 index 0000000..e49e59e --- /dev/null +++ b/examples/en/casino-and-games/04-daily-lottery/menu.conf @@ -0,0 +1,91 @@ +# Daily Lottery - global jackpot accumulator + chance-based payout +# Docs: https://abstractmenus.github.io/docs/en/examples/casino-and-games/daily-lottery +# Open: /ame_lottery +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Daily Lottery" +size: 3 +updateInterval: 40 + +activators { + command: "ame_lottery" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Jackpot display - reads the global accumulator var + { + slot: 4 + material: NETHER_STAR + name: "&6&lCurrent Jackpot" + lore: ["", "&e$%var_:ame_lottery_jackpot:0%", "", "&8Tickets cost $100. 50 of every 100 goes to jackpot."] + } + + # Buy ticket button + { + slot: 13 + material: PAPER + name: "&aBuy a ticket &8($100)" + lore: [ + "&7Click to purchase a lottery ticket." + "" + "&7You roll a 1-in-50 chance to win" + "&7the entire jackpot." + "" + "&7If you don't win, $50 of your ticket" + "&7adds to the jackpot pool." + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + # 1-in-50 chance to win the jackpot. + bulk: [ + { + rules { chance: 2 } + actions { + # Pay out the current jackpot value to the winner. + # %var_:...% inside actions is the var lookup; this passes the current value. + giveMoney: "%var_:ame_lottery_jackpot:0%" + setVar: "ame_lottery_jackpot::0" + sound: ${successSound} + broadcast: "&6&lJACKPOT! &f%player_name% &awon $%var_:ame_lottery_jackpot:0% in the lottery!" + message: "&6&lYou won the jackpot!" + } + denyActions { + # No win - $50 of the ticket price goes into the jackpot. + incVar { name: "ame_lottery_jackpot", amount: 50 } + sound: ${failSound} + message: "&7No win. $50 added to the jackpot." + } + } + ] + refreshMenu: true + } + denyActions: ${denyNoMoney} + } + } + + # Reset (admin only) - in case the menu state needs clearing in tests + { + slot: 16 + material: BARRIER + name: "&cAdmin: reset jackpot to 0" + lore: ["&8Admins only. For testing."] + rules: ${rulesAdmin} + click { + actions { + setVar: "ame_lottery_jackpot::0" + sound: ${clickSound} + message: "&aJackpot reset." + refreshMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/casino-and-games/04-daily-lottery/meta.json b/examples/en/casino-and-games/04-daily-lottery/meta.json new file mode 100644 index 0000000..e9ab29e --- /dev/null +++ b/examples/en/casino-and-games/04-daily-lottery/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Daily Lottery", + "category": "casino-and-games", + "level": "advanced", + "features": ["global-var-jackpot", "incVar-with-amount", "setVar", "chance-rule", "broadcast", "money-rule"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_lottery", + "order": 4 +} diff --git a/examples/en/casino-and-games/05-enchanter/menu.conf b/examples/en/casino-and-games/05-enchanter/menu.conf new file mode 100644 index 0000000..3cbc54b --- /dev/null +++ b/examples/en/casino-and-games/05-enchanter/menu.conf @@ -0,0 +1,102 @@ +# Random Enchanter - drag-and-drop + randActions enchant lottery +# Docs: https://abstractmenus.github.io/docs/en/examples/casino-and-games/enchanter +# Open: /ame_enchanter +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Random Enchanter" +size: 3 + +activators { + command: "ame_enchanter" +} + +# Define which slots are draggable. Slot 11 (input) and slot 15 (result). +# `x` marks draggable, `-` is regular. +draggable: [ + "---------" + "--x---x--" + "---------" +] + +# Default placeholder shown in the result slot when nothing has been enchanted yet. +resultStub { + slot: 15 + material: WHITE_STAINED_GLASS_PANE + name: "&7Result" + lore: ["&8(empty)"] +} + +# Action template: place the dragged item back into slot 15 with the chosen enchant. +# %changed_item_*% placeholders refer to whatever the player dropped into slot 11. +placeItemBase { + slot: 15 + serialized: "%changed_item_serialized%" + count: "%changed_item_amount%" +} + +# The enchant button's full click block. Defined separately so it doesn't +# clutter the items list. +enchantClick { + rules { + # Slot 11 must NOT be empty (- prefix inverts the placedItem rule). + -placedItem { + slot: 11 + material: AIR + } + } + actions { + randActions: [ + { placeItem: ${placeItemBase} { enchantments { sharpness: 1, unbreaking: 2 } } } + { placeItem: ${placeItemBase} { enchantments { efficiency: 2 } } } + { placeItem: ${placeItemBase} { enchantments { fortune: 2, unbreaking: 1 } } } + { placeItem: ${placeItemBase} { enchantments { fire_aspect: 1 } } } + ] + # Take the input item out of slot 11 (it's been "consumed"). + removePlaced: 11 + sound: ${successSound} + } + # When clicked with empty input slot, restore the result-slot stub. + denyActions { + rules { + placedItem { + slot: 15 + material: AIR + } + } + actions { + setButton: ${resultStub} + sound: ${deniedSound} + } + } +} + +items: [ + # Decorative borders around the two draggable slots + { + slot: [ + "xxxxxxxxx" + "xx-xxx-xx" + "xxxxxxxxx" + ] + material: BLACK_STAINED_GLASS_PANE + name: " " + } + + # Empty result-slot placeholder + ${resultStub} + + # The "Enchant!" button at slot 22 (bottom row, center) + { + slot: 22 + material: NETHER_STAR + name: "&bEnchant!" + lore: [ + "&7Drop a tool into the left slot," + "&7then click here to roll a random" + "&7enchant onto it." + ] + click: ${enchantClick} + } +] diff --git a/examples/en/casino-and-games/05-enchanter/meta.json b/examples/en/casino-and-games/05-enchanter/meta.json new file mode 100644 index 0000000..86b2cca --- /dev/null +++ b/examples/en/casino-and-games/05-enchanter/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Random Enchanter", + "category": "casino-and-games", + "level": "advanced", + "features": ["draggable-slots", "placeItem", "placedItem-rule", "removePlaced", "setButton", "randActions", "changed_item-placeholders"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_enchanter", + "order": 5 +} diff --git a/examples/en/cosmetics/01-tags/menu.conf b/examples/en/cosmetics/01-tags/menu.conf new file mode 100644 index 0000000..f8180d8 --- /dev/null +++ b/examples/en/cosmetics/01-tags/menu.conf @@ -0,0 +1,123 @@ +# Tag Selector - LuckPerms meta-based prefix tags +# Docs: https://abstractmenus.github.io/docs/en/examples/cosmetics/tags +# Open: /ame_tags +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Choose your tag" +size: 3 + +activators { + command: "ame_tags" +} + +# Reusable tag-application click handler. Each tag references it with the tag string overridden. +applyTag { + actions { + lpMetaSet { + metaList: [ + { key: "ame_tag", value: "%selected_tag%" } + ] + } + sound: ${successSound} + closeMenu: true + } +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Heart tag + { + slot: 11 + material: RED_DYE + name: "&c♥ Heart" + lore: ["&7Pink heart symbol next to your name."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&c♥" }] + } + sound: ${successSound} + message: "&cTag set to &c♥" + closeMenu: true + } + } + } + + # Star tag + { + slot: 12 + material: NETHER_STAR + name: "&e★ Star" + lore: ["&7Yellow star symbol."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&e★" }] + } + sound: ${successSound} + message: "&eTag set to &e★" + closeMenu: true + } + } + } + + # Crown tag - VIP only + { + slot: 13 + material: GOLDEN_HELMET + name: "&6♛ Crown &8(VIP)" + lore: ["&7Royal crown.", "", "&8Requires VIP rank"] + rules { permission: "abstractmenus.vip" } + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&6♛" }] + } + sound: ${successSound} + message: "&6Tag set to &6♛" + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + # Skull tag + { + slot: 14 + material: SKELETON_SKULL + name: "&8☠ Skull" + lore: ["&7Spooky skull symbol."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&8☠" }] + } + sound: ${successSound} + message: "&8Tag set to &8☠" + closeMenu: true + } + } + } + + # Reset / clear tag + { + slot: 15 + material: BARRIER + name: "&7Clear tag" + lore: ["&7Remove your current tag."] + click { + actions { + lpMetaRemove: ["ame_tag"] + sound: ${clickSound} + message: "&7Tag removed." + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/cosmetics/01-tags/meta.json b/examples/en/cosmetics/01-tags/meta.json new file mode 100644 index 0000000..cc76da9 --- /dev/null +++ b/examples/en/cosmetics/01-tags/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Tags", + "category": "cosmetics", + "level": "intermediate", + "features": ["lpMetaSet", "lpMetaRemove", "permission-rule", "VIP-tier"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_tags", + "order": 1 +} diff --git a/examples/en/cosmetics/02-prefixes/menu.conf b/examples/en/cosmetics/02-prefixes/menu.conf new file mode 100644 index 0000000..097fa88 --- /dev/null +++ b/examples/en/cosmetics/02-prefixes/menu.conf @@ -0,0 +1,96 @@ +# Prefixes - LuckPerms group-based prefix selector +# Docs: https://abstractmenus.github.io/docs/en/examples/cosmetics/prefixes +# Open: /ame_prefixes +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Choose your prefix" +size: 3 + +activators { + command: "ame_prefixes" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Each prefix is a LuckPerms group. Clicking adds the player to that group + # (and removes them from the others, so only one prefix is active at a time). + + { + slot: 11 + material: GREEN_WOOL + name: "&a[Member]" + lore: [ + "&7The default prefix." + "&7Free for all players." + ] + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_donor" } + { removeGroup: "ame_prefix_admin" } + { addGroup: "ame_prefix_member" } + ] + sound: ${successSound} + message: "&aPrefix set to [Member]." + closeMenu: true + } + } + } + + { + slot: 13 + material: GOLD_INGOT + name: "&6[Donor]" + lore: [ + "&7Donor prefix." + "&7Available for supporters." + "", + "&8Requires donor permission" + ] + rules { permission: "abstractmenus.donator" } + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_admin" } + { addGroup: "ame_prefix_donor" } + ] + sound: ${successSound} + message: "&6Prefix set to [Donor]." + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + { + slot: 15 + material: NETHERITE_INGOT + name: "&c[Admin]" + lore: [ + "&7Admin prefix." + "", + "&8Requires admin permission" + ] + rules { permission: "abstractmenus.admin" } + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_donor" } + { addGroup: "ame_prefix_admin" } + ] + sound: ${successSound} + message: "&cPrefix set to [Admin]." + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/cosmetics/02-prefixes/meta.json b/examples/en/cosmetics/02-prefixes/meta.json new file mode 100644 index 0000000..a0e1613 --- /dev/null +++ b/examples/en/cosmetics/02-prefixes/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Prefixes", + "category": "cosmetics", + "level": "intermediate", + "features": ["addGroup", "removeGroup", "bulk-action", "permission-rule", "exclusive-selection"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_prefixes", + "order": 2 +} diff --git a/examples/en/cosmetics/03-color-skins/menu.conf b/examples/en/cosmetics/03-color-skins/menu.conf new file mode 100644 index 0000000..0476698 --- /dev/null +++ b/examples/en/cosmetics/03-color-skins/menu.conf @@ -0,0 +1,100 @@ +# Color Skins - skin chooser using setSkin action +# Docs: https://abstractmenus.github.io/docs/en/examples/cosmetics/color-skins +# Open: /ame_skins +# Requires: SkinsRestorer + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Skin Chooser" +size: 3 + +activators { + command: "ame_skins" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: PLAYER_HEAD + name: "&6&lChoose a Skin" + lore: ["&7Pick a color to apply to your character."] + } + + # The 4 color variants. Replace texture and signature with values from + # https://mineskin.org for real skins. The placeholders below will be + # accepted by HOCON but rejected by SkinsRestorer at runtime. + + { + slot: 10 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_GOLD" + name: "&6Gold" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_GOLD" + signature: "REPLACE_WITH_SIGNATURE_GOLD" + } + sound: ${successSound} + message: "&aGold skin applied. Reconnect to see changes." + } + } + } + + { + slot: 12 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_PURPLE" + name: "&dPurple" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_PURPLE" + signature: "REPLACE_WITH_SIGNATURE_PURPLE" + } + sound: ${successSound} + message: "&dPurple skin applied. Reconnect to see changes." + } + } + } + + { + slot: 14 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_BLUE" + name: "&9Blue" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_BLUE" + signature: "REPLACE_WITH_SIGNATURE_BLUE" + } + sound: ${successSound} + message: "&9Blue skin applied. Reconnect to see changes." + } + } + } + + { + slot: 16 + material: BARRIER + name: "&7Reset to default" + lore: ["&7Restore your original skin."] + click { + actions { + closeMenu: 1 + resetSkin: true + sound: ${clickSound} + message: "&7Default skin restored. Reconnect to see changes." + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/cosmetics/03-color-skins/meta.json b/examples/en/cosmetics/03-color-skins/meta.json new file mode 100644 index 0000000..781f014 --- /dev/null +++ b/examples/en/cosmetics/03-color-skins/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Color Skins", + "category": "cosmetics", + "level": "intermediate", + "features": ["setSkin", "resetSkin", "closeMenu-before-setSkin", "PLAYER_HEAD-texture"], + "dependencies": ["SkinsRestorer"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_skins", + "order": 3 +} diff --git a/examples/en/donate/01-tiered-ranks/menu.conf b/examples/en/donate/01-tiered-ranks/menu.conf new file mode 100644 index 0000000..4fb6051 --- /dev/null +++ b/examples/en/donate/01-tiered-ranks/menu.conf @@ -0,0 +1,166 @@ +# Tiered Ranks Donate Store - 4 ranks with HAS_RANK / NO_RANK dual items +# Docs: https://abstractmenus.github.io/docs/en/examples/donate/tiered-ranks +# Open: /ame_donate +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Donate Store - Ranks" +size: 5 + +activators { + command: ["ame_donate", "ame_support"] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "36-44" } + + # Header + { + slot: 4 + material: NETHER_STAR + name: "&6&lSupport MyTestServer" + lore: ["&7Pick a rank to learn more.", "&7Buying ranks supports the server."] + } + + # ========== Knight ========== + # HAS_RANK variant: player already has knight permission, can't buy again. + { + slot: 19 + material: IRON_SWORD + name: "&7Knight &8(&aOwned&8)" + lore: [ + "&7Tier 1 supporter rank." + "" + "&aYou already own this rank." + "&7Consider upgrading below." + ] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.knight" } + } + # NO_RANK variant: doesn't have it yet, show price. + { + slot: 19 + material: IRON_SWORD + name: "&7Knight" + lore: [ + "&7Tier 1 supporter rank." + "" + "&7Includes:" + "&7- &fcolored chat name" + "&7- &f3 home points" + "" + "&ePrice: &a$5" + "" + "&aLeft-click to learn more" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + # In production, this would open a confirm/purchase URL menu. + # For the example, just inform. + message: "&aOpen the donate page at https://example.org to purchase Knight." + sound: ${clickSound} + } + } + } + + # ========== Paladin ========== + { + slot: 21 + material: GOLDEN_SWORD + name: "&6Paladin &8(&aOwned&8)" + lore: ["&7Tier 2 supporter rank.", "", "&aYou already own this rank."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.paladin" } + } + { + slot: 21 + material: GOLDEN_SWORD + name: "&6Paladin" + lore: [ + "&7Tier 2 supporter rank." + "" + "&7Includes:" + "&7- &feverything from Knight" + "&7- &f5 home points" + "&7- &f/fly in build worlds" + "" + "&ePrice: &a$15" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aOpen the donate page at https://example.org to purchase Paladin." + sound: ${clickSound} + } + } + } + + # ========== Lord ========== + { + slot: 23 + material: DIAMOND_SWORD + name: "&bLord &8(&aOwned&8)" + lore: ["&7Tier 3 supporter rank.", "", "&aYou already own this rank."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.lord" } + } + { + slot: 23 + material: DIAMOND_SWORD + name: "&bLord" + lore: [ + "&7Tier 3 supporter rank." + "" + "&7Includes:" + "&7- &feverything from Paladin" + "&7- &fcustom join messages" + "&7- &f10 home points" + "" + "&ePrice: &a$30" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aOpen the donate page at https://example.org to purchase Lord." + sound: ${clickSound} + } + } + } + + # ========== Monarch ========== + { + slot: 25 + material: NETHERITE_SWORD + name: "&dMonarch &8(&aOwned&8)" + lore: ["&7Tier 4 supporter rank.", "", "&aYou already own this rank."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.monarch" } + } + { + slot: 25 + material: NETHERITE_SWORD + name: "&dMonarch" + lore: [ + "&7Tier 4 supporter rank, the highest tier." + "" + "&7Includes:" + "&7- &feverything from Lord" + "&7- &fparticle effect on join" + "&7- &fpriority queue when full" + "" + "&ePrice: &a$60" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aOpen the donate page at https://example.org to purchase Monarch." + sound: ${clickSound} + } + } + } + + ${buttonClose} { slot: 40 } +] diff --git a/examples/en/donate/01-tiered-ranks/meta.json b/examples/en/donate/01-tiered-ranks/meta.json new file mode 100644 index 0000000..fbc65d7 --- /dev/null +++ b/examples/en/donate/01-tiered-ranks/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Tiered Donate Ranks", + "category": "donate", + "level": "intermediate", + "features": ["dual-item-display", "permission-rule", "command-aliases", "tier-pricing"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_donate", + "order": 1 +} diff --git a/examples/en/donate/02-item-store/menu.conf b/examples/en/donate/02-item-store/menu.conf new file mode 100644 index 0000000..fed4a0c --- /dev/null +++ b/examples/en/donate/02-item-store/menu.conf @@ -0,0 +1,111 @@ +# Donate Item Store - buy in-game items with donor tokens +# Docs: https://abstractmenus.github.io/docs/en/examples/donate/item-store +# Open: /ame_store +# Requires: Vault (or PlayerPoints addon for donor tokens) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Donate Item Store" +size: 3 + +activators { + command: "ame_store" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Header + { + slot: 4 + material: NETHER_STAR + name: "&6&lDonate Items" + lore: ["&7Special items purchasable with", "&7donor currency."] + } + + # Mythic sword + { + slot: 11 + material: NETHERITE_SWORD + name: "&dMythic Sword" + lore: [ + "&7A unique enchanted sword." + "" + "&7Sharpness V" + "&7Unbreaking III" + "&7Mending" + "" + "&ePrice: &a1000 tokens" + ] + flags: HIDE_ATTRIBUTES + click { + rules { money: 1000 } + actions { + takeMoney: 1000 + itemAdd { + material: NETHERITE_SWORD + name: "&dMythic Sword" + enchantments { + sharpness: 5 + unbreaking: 3 + mending: 1 + } + } + sound: ${successSound} + message: "&dYou received the Mythic Sword!" + } + denyActions: ${denyNoMoney} + } + } + + # Permanent fly token + { + slot: 13 + material: ELYTRA + name: "&bElytra" + lore: [ + "&7Vanilla elytra for survival flight." + "" + "&ePrice: &a500 tokens" + ] + flags: HIDE_ATTRIBUTES + click { + rules { money: 500 } + actions { + takeMoney: 500 + itemAdd { material: ELYTRA } + sound: ${successSound} + message: "&aElytra added to inventory." + } + denyActions: ${denyNoMoney} + } + } + + # Heads pack + { + slot: 15 + material: PLAYER_HEAD + name: "&6Cosmetic Heads Pack (5x)" + lore: [ + "&7Five custom-textured player heads" + "&7for decoration." + "" + "&ePrice: &a100 tokens" + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd: [ + { material: PLAYER_HEAD, count: 5 } + ] + sound: ${successSound} + message: "&aHeads pack delivered." + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/donate/02-item-store/meta.json b/examples/en/donate/02-item-store/meta.json new file mode 100644 index 0000000..e491904 --- /dev/null +++ b/examples/en/donate/02-item-store/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Item Store", + "category": "donate", + "level": "intermediate", + "features": ["money-rule", "takeMoney", "itemAdd-with-enchantments", "donor-currency"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_store", + "order": 2 +} diff --git a/examples/en/hub-and-nav/01-main-hub/menu.conf b/examples/en/hub-and-nav/01-main-hub/menu.conf new file mode 100644 index 0000000..0f56dab --- /dev/null +++ b/examples/en/hub-and-nav/01-main-hub/menu.conf @@ -0,0 +1,82 @@ +# Main Hub - launcher for the example pack +# Docs: https://abstractmenus.github.io/docs/en/examples/hub-and-nav/main-hub +# Open: /ame_hub +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Example Pack - Main Hub" +size: 3 + +activators { + command: "ame_hub" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Header + { + slot: 4 + material: NETHER_STAR + name: "&6&lAbstractMenus Example Pack" + lore: ["&7Click any tile to open that example."] + } + + # Shops + { + slot: 10 + material: CAKE + name: "&aCake Shop" + lore: ["&7A minimal money + itemAdd shop.", "", "&8Opens /ame_cake_shop"] + click { + openMenu: "ame_cake_shop" + } + } + + { + slot: 11 + material: BREAD + name: "&aMulti-Category Shop" + lore: ["&7Hub + sub-shops navigation.", "", "&8Opens /ame_shop"] + click { + openMenu: "ame_shop" + } + } + + # State + { + slot: 12 + material: BOOK + name: "&aCounter & Toggle" + lore: ["&7Per-player vars in action.", "", "&8Opens /ame_counter"] + click { + openMenu: "ame_counter" + } + } + + # Info + { + slot: 13 + material: WRITTEN_BOOK + name: "&aServer Rules" + lore: ["&7Read-only example menu.", "", "&8Opens /ame_rules"] + click { + openMenu: "ame_rules" + } + } + + # Kits + { + slot: 14 + material: CHEST + name: "&aBasic Kit" + lore: ["&7One-click reward bundle.", "", "&8Opens /ame_kit"] + click { + openMenu: "ame_kit" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/hub-and-nav/01-main-hub/meta.json b/examples/en/hub-and-nav/01-main-hub/meta.json new file mode 100644 index 0000000..adbbf84 --- /dev/null +++ b/examples/en/hub-and-nav/01-main-hub/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Main Hub", + "category": "hub-and-nav", + "level": "beginner", + "features": ["openMenu", "menu-launcher"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_hub", + "order": 1 +} diff --git a/examples/en/hub-and-nav/02-sub-hub/menu.conf b/examples/en/hub-and-nav/02-sub-hub/menu.conf new file mode 100644 index 0000000..cecab56 --- /dev/null +++ b/examples/en/hub-and-nav/02-sub-hub/menu.conf @@ -0,0 +1,64 @@ +# Sub-Hub - secondary hub with a back button to the main hub +# Docs: https://abstractmenus.github.io/docs/en/examples/hub-and-nav/sub-hub +# Open: /ame_subhub OR click the "Other" tile in the main hub +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Other Features" +size: 3 + +activators { + command: "ame_subhub" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Header + { + slot: 4 + material: NETHER_STAR + name: "&6&lOther Features" + lore: ["&7Things that didn't fit on the main hub."] + } + + # Tag chooser + { + slot: 11 + material: PAPER + name: "&aTags" + lore: ["&7Pick a chat tag.", "", "&8Opens /ame_tags"] + click { openMenu: "ame_tags" } + } + + # Roulette + { + slot: 13 + material: NETHER_STAR + name: "&aRoulette" + lore: ["&7Try your luck.", "", "&8Opens /ame_roulette"] + click { openMenu: "ame_roulette" } + } + + # Vote + { + slot: 15 + material: BEACON + name: "&aDaily vote" + lore: ["&7Vote and earn rewards.", "", "&8Opens /ame_vote"] + click { openMenu: "ame_vote" } + } + + # Back to main hub + { + slot: 18 + material: ARROW + name: "&eBack to Main Hub" + lore: ["&7Return to /ame_hub."] + click { openMenu: "ame_hub" } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/hub-and-nav/02-sub-hub/meta.json b/examples/en/hub-and-nav/02-sub-hub/meta.json new file mode 100644 index 0000000..8742098 --- /dev/null +++ b/examples/en/hub-and-nav/02-sub-hub/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Sub-Hub", + "category": "hub-and-nav", + "level": "beginner", + "features": ["openMenu", "back-button-pattern", "secondary-hub"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_subhub", + "order": 2 +} diff --git a/examples/en/hub-and-nav/03-breadcrumb-chain/menu.conf b/examples/en/hub-and-nav/03-breadcrumb-chain/menu.conf new file mode 100644 index 0000000..0251801 --- /dev/null +++ b/examples/en/hub-and-nav/03-breadcrumb-chain/menu.conf @@ -0,0 +1,71 @@ +# Breadcrumb Chain - 3 nested menus using openMenuCtx for dynamic back navigation +# Docs: https://abstractmenus.github.io/docs/en/examples/hub-and-nav/breadcrumb-chain +# Open: /ame_chain +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Level 1 + ame_chain_l1 { + title: "&6Level 1" + size: 1 + activators { command: "ame_chain" } + items: [ + { + slot: 4 + material: BLUE_WOOL + name: "&aGo deeper" + lore: ["&7Open Level 2."] + click { openMenu: "ame_chain_l2" } + } + ${buttonClose} { slot: 8 } + ] + } + + # Level 2 + ame_chain_l2 { + title: "&6Level 2" + size: 1 + items: [ + { + slot: 4 + material: GREEN_WOOL + name: "&aGo deeper" + lore: ["&7Open Level 3."] + click { openMenu: "ame_chain_l3" } + } + { + slot: 0 + material: ARROW + name: "&eBack to Level 1" + click { openMenu: "ame_chain_l1" } + } + ] + } + + # Level 3 + ame_chain_l3 { + title: "&6Level 3 (deepest)" + size: 1 + items: [ + { + slot: 4 + material: GOLD_BLOCK + name: "&6You reached the bottom" + lore: [ + "&7There's no Level 4." + "&7Use the back arrow to climb out." + ] + } + { + slot: 0 + material: ARROW + name: "&eBack to Level 2" + click { openMenu: "ame_chain_l2" } + } + ] + } + +} diff --git a/examples/en/hub-and-nav/03-breadcrumb-chain/meta.json b/examples/en/hub-and-nav/03-breadcrumb-chain/meta.json new file mode 100644 index 0000000..9d5a9cd --- /dev/null +++ b/examples/en/hub-and-nav/03-breadcrumb-chain/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Breadcrumb Chain", + "category": "hub-and-nav", + "level": "intermediate", + "features": ["multi-menu-file", "openMenu-chain", "manual-back-navigation"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_chain", + "order": 3 +} diff --git a/examples/en/info-pages/01-rules/menu.conf b/examples/en/info-pages/01-rules/menu.conf new file mode 100644 index 0000000..82536b9 --- /dev/null +++ b/examples/en/info-pages/01-rules/menu.conf @@ -0,0 +1,91 @@ +# Server Rules - read-only info menu +# Docs: https://abstractmenus.github.io/docs/en/examples/info-pages/rules +# Open: /ame_rules +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Server Rules" +size: 5 + +activators { + command: "ame_rules" +} + +items: [ + # Decorative borders + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "36-44" } + + # Header + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lRules of MyTestServer" + lore: [ + "&7Read carefully." + "&7Breaking any rule may lead to a ban." + ] + } + + # Rule 1 + { + slot: 19 + material: PAPER + name: "&e1. No griefing" + lore: [ + "&7Do not destroy or modify" + "&7other players' builds without" + "&7their explicit permission." + "" + "&8First offense: warning" + "&8Repeat: 7-day ban" + ] + } + + # Rule 2 + { + slot: 21 + material: PAPER + name: "&e2. Be civil in chat" + lore: [ + "&7No slurs, no harassment," + "&7no spam. English in main chat," + "&7other languages in their channels." + "" + "&8First offense: 1h mute" + "&8Repeat: permanent mute" + ] + } + + # Rule 3 + { + slot: 23 + material: PAPER + name: "&e3. No cheating" + lore: [ + "&7Hacked clients, x-ray, autoclicker," + "&7and similar mods are banned." + "&7Optifine and shaders are fine." + "" + "&8First offense: permanent ban" + ] + } + + # Rule 4 + { + slot: 25 + material: PAPER + name: "&e4. No real-money trade" + lore: [ + "&7Selling in-game items, ranks, or" + "&7services for real money is banned." + "&7Use the official donate menu only." + "" + "&8First offense: permanent ban" + ] + } + + # Close button + ${buttonClose} { slot: 40 } +] diff --git a/examples/en/info-pages/01-rules/meta.json b/examples/en/info-pages/01-rules/meta.json new file mode 100644 index 0000000..27a2661 --- /dev/null +++ b/examples/en/info-pages/01-rules/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Server Rules", + "category": "info-pages", + "level": "beginner", + "features": ["lore", "static-display", "no-click-handlers"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_rules", + "order": 1 +} diff --git a/examples/en/info-pages/02-server-links/menu.conf b/examples/en/info-pages/02-server-links/menu.conf new file mode 100644 index 0000000..f4460ca --- /dev/null +++ b/examples/en/info-pages/02-server-links/menu.conf @@ -0,0 +1,91 @@ +# Server Links - external links via openBook +# Docs: https://abstractmenus.github.io/docs/en/examples/info-pages/server-links +# Open: /ame_links +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Useful Links" +size: 3 + +activators { + command: "ame_links" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Header + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lMyTestServer Resources" + lore: ["&7Click any tile for the link.", "&7Books open clickable URLs."] + } + + # Discord - opens a written book with the invite URL. + { + slot: 11 + material: PURPLE_DYE + name: "&5Discord" + lore: ["&7Join our Discord server.", "", "&aClick to open the link"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&5Discord" + pages: [ + "Click the link below to open our Discord invite:\n\nhttps://discord.gg/example" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + # Website + { + slot: 13 + material: COMPASS + name: "&aWebsite" + lore: ["&7Visit our website.", "", "&aClick to open the link"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&aWebsite" + pages: [ + "Click the link below to visit our site:\n\nhttps://example.org" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + # GitHub / source + { + slot: 15 + material: WRITABLE_BOOK + name: "&8GitHub" + lore: ["&7Source code and issue tracker.", "", "&aClick to open the link"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&8GitHub" + pages: [ + "Click the link below to open our GitHub:\n\nhttps://github.com/example" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/info-pages/02-server-links/meta.json b/examples/en/info-pages/02-server-links/meta.json new file mode 100644 index 0000000..a0e7303 --- /dev/null +++ b/examples/en/info-pages/02-server-links/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Server Links", + "category": "info-pages", + "level": "intermediate", + "features": ["openBook", "external-links", "closeMenu-with-action"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_links", + "order": 2 +} diff --git a/examples/en/info-pages/03-faq/menu.conf b/examples/en/info-pages/03-faq/menu.conf new file mode 100644 index 0000000..2fc7144 --- /dev/null +++ b/examples/en/info-pages/03-faq/menu.conf @@ -0,0 +1,137 @@ +# FAQ - hub menu of questions, click each to open the answer in a sub-menu +# Docs: https://abstractmenus.github.io/docs/en/examples/info-pages/faq +# Open: /ame_faq +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Main FAQ index - list of questions + ame_faq { + title: "&6Frequently Asked Questions" + size: 3 + + activators { + command: "ame_faq" + } + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lFAQ" + lore: ["&7Click any question for the answer."] + } + + { + slot: 10 + material: PAPER + name: "&aHow do I claim land?" + click { openMenu: "ame_faq_a1" } + } + { + slot: 12 + material: PAPER + name: "&aWhat does VIP rank give me?" + click { openMenu: "ame_faq_a2" } + } + { + slot: 14 + material: PAPER + name: "&aHow do I report a player?" + click { openMenu: "ame_faq_a3" } + } + { + slot: 16 + material: PAPER + name: "&aWhere are server rules?" + click { openMenu: "ame_rules" } + } + + ${buttonClose} { slot: 22 } + ] + } + + # Answer 1 - Land claiming + ame_faq_a1 { + title: "&6FAQ - Land claiming" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: GRASS_BLOCK + name: "&aHow do I claim land?" + lore: [ + "&7Use a golden shovel to mark" + "&7the corners of your claim." + "" + "&7- Right-click first corner" + "&7- Right-click opposite corner" + "" + "&7Each block costs 1 claim block." + "&7Earn more by playing or buying" + "&7with /buyclaimblocks." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + + # Answer 2 - VIP perks + ame_faq_a2 { + title: "&6FAQ - VIP perks" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: GOLDEN_HELMET + name: "&6What does VIP give me?" + lore: [ + "&7VIP includes:" + "" + "&7- &fcolored chat name" + "&7- &f5 home points (vs 1 default)" + "&7- &fdaily VIP kit" + "&7- &f50% shop discount" + "&7- &fparticle effect on join" + "" + "&7Get VIP at &e/ame_donate&7." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + + # Answer 3 - Reporting players + ame_faq_a3 { + title: "&6FAQ - Reporting" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: BARRIER + name: "&cReporting players" + lore: [ + "&7Use &e/report &7" + "&7or post in our Discord at" + "&7&fhttps://discord.gg/example" + "" + "&7Include screenshots if possible." + "&7Staff respond within a few hours." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + +} diff --git a/examples/en/info-pages/03-faq/meta.json b/examples/en/info-pages/03-faq/meta.json new file mode 100644 index 0000000..e82c7ab --- /dev/null +++ b/examples/en/info-pages/03-faq/meta.json @@ -0,0 +1,12 @@ +{ + "title": "FAQ", + "category": "info-pages", + "level": "intermediate", + "features": ["multi-menu-file", "openMenu-navigation", "back-button-pattern", "menu-tree"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_faq", + "order": 3 +} diff --git a/examples/en/kits-and-rewards/01-basic-kit/menu.conf b/examples/en/kits-and-rewards/01-basic-kit/menu.conf new file mode 100644 index 0000000..ed2e222 --- /dev/null +++ b/examples/en/kits-and-rewards/01-basic-kit/menu.conf @@ -0,0 +1,60 @@ +# Basic Kit - one-click reward bundle +# Docs: https://abstractmenus.github.io/docs/en/examples/kits-and-rewards/basic-kit +# Open: /ame_kit +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Starter Kit" +size: 3 + +activators { + command: "ame_kit" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Description card + { + slot: 4 + material: PAPER + name: "&6&lStarter Kit" + lore: [ + "&7Get a one-time bundle of starter items" + "&7and 50 XP to begin your adventure." + "" + "&eContents:" + "&7- Iron Sword" + "&7- Iron Pickaxe" + "&7- Bread x16" + "&7- 50 XP" + ] + } + + # Claim button + { + slot: 13 + material: CHEST + name: "&aClaim Kit" + lore: [ + "&7Click to receive your kit." + ] + click { + actions { + itemAdd: [ + { material: IRON_SWORD } + { material: IRON_PICKAXE } + { material: BREAD, count: 16 } + ] + giveXp: 50 + sound: ${successSound} + message: "&aYou claimed the Starter Kit!" + closeMenu: 20 + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/kits-and-rewards/01-basic-kit/meta.json b/examples/en/kits-and-rewards/01-basic-kit/meta.json new file mode 100644 index 0000000..ed7070a --- /dev/null +++ b/examples/en/kits-and-rewards/01-basic-kit/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Basic Kit", + "category": "kits-and-rewards", + "level": "beginner", + "features": ["itemAdd", "giveXp", "sound", "message", "closeMenu-delay"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_kit", + "order": 1 +} diff --git a/examples/en/kits-and-rewards/02-daily-kit-cooldown/menu.conf b/examples/en/kits-and-rewards/02-daily-kit-cooldown/menu.conf new file mode 100644 index 0000000..3f97207 --- /dev/null +++ b/examples/en/kits-and-rewards/02-daily-kit-cooldown/menu.conf @@ -0,0 +1,132 @@ +# Daily Kit with Cooldown - temporal vars + dual-item pattern +# Docs: https://abstractmenus.github.io/docs/en/examples/kits-and-rewards/daily-kit-cooldown +# Open: /ame_daily_kit +# Requires: LuckPerms (for group rule on the VIP kit) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Daily Kits" +size: 3 +updateInterval: 20 + +activators { + command: "ame_daily_kit" +} + +# Template definitions - shared between the "ready" and "on cooldown" item variants + +defaultKitTemplate { + slot: 11 + material: WOODEN_SWORD + name: "&aDefault Kit" + flags: HIDE_ATTRIBUTES + lore: [ + "&7Includes:" + "&7- Leather armor set" + "&7- Wooden sword" + "&7- 32 cooked beef" + "&7- 16 iron ingots" + ] +} + +vipKitTemplate { + slot: 15 + material: STONE_SWORD + name: "&6VIP Kit" + flags: HIDE_ATTRIBUTES + lore: [ + "&7Includes:" + "&7- Leather armor set" + "&7- Stone sword" + "&7- 32 cooked beef" + "&7- 32 iron ingots" + "&7- 4 golden apples" + "" + "&8Requires: VIP group" + ] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Default kit ========== + # READY state: shown when no cooldown variable exists. Clicking claims the kit. + ${defaultKitTemplate} { + lore: ${defaultKitTemplate.lore} [ + "" + "&aClick to claim" + ] + click { + actions { + itemAdd: [ + { material: LEATHER_HELMET } + { material: LEATHER_CHESTPLATE } + { material: LEATHER_LEGGINGS } + { material: LEATHER_BOOTS } + { material: WOODEN_SWORD } + { material: COOKED_BEEF, count: 32 } + { material: IRON_INGOT, count: 16 } + ] + # Set a temporal var that expires after 1 day - the ::1d suffix. + setVarp: "ame_kit_default::1::1d" + sound: ${successSound} + message: "&aDefault kit claimed. Come back in 24 hours." + refreshMenu: true + } + } + } + # COOLDOWN state: shown only while the temporal var exists. Displays time remaining. + ${defaultKitTemplate} { + material: GRAY_DYE + lore: ${defaultKitTemplate.lore} [ + "" + "&cAvailable in &e%varpt_:ame_kit_default%" + ] + rules { + existVarp: "ame_kit_default" + } + } + + # ========== VIP kit ========== + ${vipKitTemplate} { + lore: ${vipKitTemplate.lore} [ + "" + "&aClick to claim" + ] + click { + rules { + group: "vip" + } + actions { + itemAdd: [ + { material: LEATHER_HELMET } + { material: LEATHER_CHESTPLATE } + { material: LEATHER_LEGGINGS } + { material: LEATHER_BOOTS } + { material: STONE_SWORD } + { material: COOKED_BEEF, count: 32 } + { material: IRON_INGOT, count: 32 } + { material: GOLDEN_APPLE, count: 4 } + ] + setVarp: "ame_kit_vip::1::1d" + sound: ${successSound} + message: "&6VIP kit claimed. Come back in 24 hours." + refreshMenu: true + } + denyActions: ${denyNoPerm} + } + } + ${vipKitTemplate} { + material: GRAY_DYE + lore: ${vipKitTemplate.lore} [ + "" + "&cAvailable in &e%varpt_:ame_kit_vip%" + ] + rules { + existVarp: "ame_kit_vip" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/kits-and-rewards/02-daily-kit-cooldown/meta.json b/examples/en/kits-and-rewards/02-daily-kit-cooldown/meta.json new file mode 100644 index 0000000..62be528 --- /dev/null +++ b/examples/en/kits-and-rewards/02-daily-kit-cooldown/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Daily Kit with Cooldown", + "category": "kits-and-rewards", + "level": "advanced", + "features": ["setVarp-temporal", "existVarp", "varpt-placeholder", "group-rule", "template-substitution", "lore-extension", "dual-item-display"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_daily_kit", + "order": 2 +} diff --git a/examples/en/kits-and-rewards/03-tiered-kits/menu.conf b/examples/en/kits-and-rewards/03-tiered-kits/menu.conf new file mode 100644 index 0000000..7c4af00 --- /dev/null +++ b/examples/en/kits-and-rewards/03-tiered-kits/menu.conf @@ -0,0 +1,114 @@ +# Tiered Kits - 3 kit tiers gated by permission, no cooldown +# Docs: https://abstractmenus.github.io/docs/en/examples/kits-and-rewards/tiered-kits +# Open: /ame_kit_tiers +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Tiered Kits" +size: 3 + +activators { + command: "ame_kit_tiers" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Default kit (everyone) ========== + { + slot: 11 + material: WOODEN_SWORD + name: "&7Default Kit" + flags: HIDE_ATTRIBUTES + lore: [ + "&7Includes:" + "&7- Wooden sword" + "&7- 8 bread" + "" + "&aFree for everyone" + "&aClick to claim" + ] + click { + actions { + itemAdd: [ + { material: WOODEN_SWORD } + { material: BREAD, count: 8 } + ] + sound: ${successSound} + message: "&aDefault kit claimed." + } + } + } + + # ========== VIP kit (permission-gated) ========== + { + slot: 13 + material: STONE_SWORD + name: "&6VIP Kit" + flags: HIDE_ATTRIBUTES + lore: [ + "&7Includes:" + "&7- Stone sword" + "&7- Iron armor (full set)" + "&7- 16 bread, 4 golden apples" + "" + "&8Requires: VIP rank" + "&aClick to claim" + ] + click { + rules { permission: "abstractmenus.vip" } + actions { + itemAdd: [ + { material: STONE_SWORD } + { material: IRON_HELMET } + { material: IRON_CHESTPLATE } + { material: IRON_LEGGINGS } + { material: IRON_BOOTS } + { material: BREAD, count: 16 } + { material: GOLDEN_APPLE, count: 4 } + ] + sound: ${successSound} + message: "&6VIP kit claimed." + } + denyActions: ${denyNoPerm} + } + } + + # ========== Elite kit (higher permission) ========== + { + slot: 15 + material: DIAMOND_SWORD + name: "&dElite Kit" + flags: HIDE_ATTRIBUTES + lore: [ + "&7Includes:" + "&7- Diamond sword" + "&7- Diamond armor (full set)" + "&7- 32 bread, 8 enchanted golden apples" + "" + "&8Requires: Elite rank" + "&aClick to claim" + ] + click { + rules { permission: "abstractmenus.elite" } + actions { + itemAdd: [ + { material: DIAMOND_SWORD } + { material: DIAMOND_HELMET } + { material: DIAMOND_CHESTPLATE } + { material: DIAMOND_LEGGINGS } + { material: DIAMOND_BOOTS } + { material: BREAD, count: 32 } + { material: ENCHANTED_GOLDEN_APPLE, count: 8 } + ] + sound: ${successSound} + message: "&dElite kit claimed." + } + denyActions: ${denyNoPerm} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/kits-and-rewards/03-tiered-kits/meta.json b/examples/en/kits-and-rewards/03-tiered-kits/meta.json new file mode 100644 index 0000000..7827973 --- /dev/null +++ b/examples/en/kits-and-rewards/03-tiered-kits/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Tiered Kits", + "category": "kits-and-rewards", + "level": "intermediate", + "features": ["itemAdd-list", "permission-rule", "denyActions-substitution", "no-cooldown"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_kit_tiers", + "order": 3 +} diff --git a/examples/en/shops/01-cake-shop/menu.conf b/examples/en/shops/01-cake-shop/menu.conf new file mode 100644 index 0000000..86228ec --- /dev/null +++ b/examples/en/shops/01-cake-shop/menu.conf @@ -0,0 +1,101 @@ +# Cake Shop - basic shop example +# Docs: https://abstractmenus.github.io/docs/en/examples/shops/cake-shop +# Open: /ame_cake_shop +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Cake Shop" +size: 3 + +activators { + command: "ame_cake_shop" +} + +items: [ + # Top and bottom borders, using a shared template + slot range + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Decorative header in the middle of the top row + ${commonHeader} { slot: 4 } + + # Buy a single cake for $100 + { + slot: 11 + material: CAKE + name: "&6Sweet Cake" + lore: [ + "&7A delicious cake." + "" + "&ePrice: &a$100" + "" + "&8Click to buy" + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd { material: CAKE } + sound: ${successSound} + message: "&aYou bought a Cake!" + } + denyActions: ${denyNoMoney} + } + } + + # Buy 4 cookies for $25 + { + slot: 13 + material: COOKIE + count: 4 + name: "&6Cookie x4" + lore: [ + "&7A pack of four cookies." + "" + "&ePrice: &a$25" + "" + "&8Click to buy" + ] + click { + rules { money: 25 } + actions { + takeMoney: 25 + itemAdd { + material: COOKIE + count: 4 + } + sound: ${successSound} + message: "&aYou bought 4 Cookies!" + } + denyActions: ${denyNoMoney} + } + } + + # Buy a pumpkin pie for $60 + { + slot: 15 + material: PUMPKIN_PIE + name: "&6Pumpkin Pie" + lore: [ + "&7A seasonal favorite." + "" + "&ePrice: &a$60" + "" + "&8Click to buy" + ] + click { + rules { money: 60 } + actions { + takeMoney: 60 + itemAdd { material: PUMPKIN_PIE } + sound: ${successSound} + message: "&aYou bought a Pumpkin Pie!" + } + denyActions: ${denyNoMoney} + } + } + + # Close button + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/shops/01-cake-shop/meta.json b/examples/en/shops/01-cake-shop/meta.json new file mode 100644 index 0000000..7ce865c --- /dev/null +++ b/examples/en/shops/01-cake-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Cake Shop", + "category": "shops", + "level": "beginner", + "features": ["money", "takeMoney", "itemAdd"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_cake_shop", + "order": 1 +} diff --git a/examples/en/shops/02-paginated-shop/menu.conf b/examples/en/shops/02-paginated-shop/menu.conf new file mode 100644 index 0000000..846eaec --- /dev/null +++ b/examples/en/shops/02-paginated-shop/menu.conf @@ -0,0 +1,201 @@ +# Multi-Category Shop - hub + sub-shops navigation pattern +# Docs: https://abstractmenus.github.io/docs/en/examples/shops/paginated-shop +# Open: /ame_shop +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Main hub: lands here from /ame_shop. Tiles open sub-shops. + ame_shop { + title: "&6Items Shop" + size: 1 + + activators { + command: "ame_shop" + } + + items: [ + { + slot: 2 + material: BREAD + name: "&aFood" + lore: ["&7Bread, apples, golden carrots."] + click { + openMenu: "ame_shop_food" + } + } + { + slot: 6 + material: IRON_PICKAXE + name: "&aTools" + lore: ["&7Pickaxes, axes, shovels."] + flags: HIDE_ATTRIBUTES + click { + openMenu: "ame_shop_tools" + } + } + ${buttonClose} { slot: 8 } + ] + } + + # Food sub-shop: 4 items demonstrating the buy pattern. + # The pattern repeats for any other category - just copy this menu and swap items. + ame_shop_food { + title: "&6Shop > Food" + size: 3 + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-25" } + + { + slot: 10 + material: APPLE + count: 16 + name: "&aApple x16" + lore: ["", "&ePrice: &a$50", "", "&8Click to buy"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: APPLE, count: 16 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 12 + material: BREAD + count: 8 + name: "&aBread x8" + lore: ["", "&ePrice: &a$60", "", "&8Click to buy"] + click { + rules { money: 60 } + actions { + takeMoney: 60 + itemAdd { material: BREAD, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 14 + material: COOKED_BEEF + count: 8 + name: "&aSteak x8" + lore: ["", "&ePrice: &a$120", "", "&8Click to buy"] + click { + rules { money: 120 } + actions { + takeMoney: 120 + itemAdd { material: COOKED_BEEF, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 16 + material: GOLDEN_CARROT + count: 4 + name: "&6Golden Carrot x4" + lore: ["", "&ePrice: &a$300", "", "&8Click to buy"] + click { + rules { money: 300 } + actions { + takeMoney: 300 + itemAdd { material: GOLDEN_CARROT, count: 4 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + # Back to the main hub + { + slot: 26 + material: ARROW + name: "&eBack" + lore: ["&7Return to the main shop"] + click { + openMenu: "ame_shop" + } + } + ] + } + + # Tools sub-shop: 3 items showcasing flags + higher prices. + ame_shop_tools { + title: "&6Shop > Tools" + size: 3 + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-25" } + + { + slot: 11 + material: STONE_PICKAXE + name: "&aStone Pickaxe" + lore: ["", "&ePrice: &a$200", "", "&8Click to buy"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 200 } + actions { + takeMoney: 200 + itemAdd { material: STONE_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 13 + material: IRON_PICKAXE + name: "&aIron Pickaxe" + lore: ["", "&ePrice: &a$800", "", "&8Click to buy"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 800 } + actions { + takeMoney: 800 + itemAdd { material: IRON_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 15 + material: DIAMOND_PICKAXE + name: "&bDiamond Pickaxe" + lore: ["", "&ePrice: &a$3000", "", "&8Click to buy"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 3000 } + actions { + takeMoney: 3000 + itemAdd { material: DIAMOND_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + { + slot: 26 + material: ARROW + name: "&eBack" + lore: ["&7Return to the main shop"] + click { + openMenu: "ame_shop" + } + } + ] + } + +} diff --git a/examples/en/shops/02-paginated-shop/meta.json b/examples/en/shops/02-paginated-shop/meta.json new file mode 100644 index 0000000..60738db --- /dev/null +++ b/examples/en/shops/02-paginated-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Multi-Category Shop", + "category": "shops", + "level": "intermediate", + "features": ["money", "takeMoney", "itemAdd", "openMenu", "menus-block"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_shop", + "order": 2 +} diff --git a/examples/en/shops/03-rank-gated-shop/menu.conf b/examples/en/shops/03-rank-gated-shop/menu.conf new file mode 100644 index 0000000..9275f89 --- /dev/null +++ b/examples/en/shops/03-rank-gated-shop/menu.conf @@ -0,0 +1,80 @@ +# Rank-Gated Shop - dual-item HAS/NO_PERM display pattern +# Docs: https://abstractmenus.github.io/docs/en/examples/shops/rank-gated-shop +# Open: /ame_rank_shop +# Requires: Vault, LuckPerms (or any permissions provider) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Rank Shop" +size: 3 + +activators { + command: "ame_rank_shop" +} + +# Reusable click handler for the VIP-discount line - takes the discounted price. +discountClick { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + message: "&aPurchased with VIP discount." + } + denyActions: ${denyNoMoney} +} + +# Reusable click handler for the regular price. +regularClick { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + message: "&aPurchased at regular price." + } + denyActions: ${denyNoMoney} +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Golden Apple - dual variants by VIP permission ========== + + # VIP variant: discounted, shown only to VIP players. + { + slot: 13 + material: GOLDEN_APPLE + name: "&6Golden Apple &8(&aVIP price&8)" + lore: [ + "&7Premium fruit." + "" + "&ePrice: &a$50 &m$100" + "&8(VIP 50% discount)" + "" + "&aClick to buy" + ] + rules { permission: "abstractmenus.vip" } + click: ${discountClick} + } + + # Regular variant: full price, shown to everyone else (no rules = always visible, + # but shadowed by the VIP variant when VIP rule passes). + { + slot: 13 + material: GOLDEN_APPLE + name: "&6Golden Apple" + lore: [ + "&7Premium fruit." + "" + "&ePrice: &a$100" + "" + "&aClick to buy" + "&8VIP rank gives 50% off." + ] + click: ${regularClick} + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/shops/03-rank-gated-shop/meta.json b/examples/en/shops/03-rank-gated-shop/meta.json new file mode 100644 index 0000000..91fe0df --- /dev/null +++ b/examples/en/shops/03-rank-gated-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rank-Gated Shop", + "category": "shops", + "level": "intermediate", + "features": ["dual-item-display", "permission-rule", "money-rule", "discount-tier", "click-handler-template"], + "dependencies": ["Vault", "LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_rank_shop", + "order": 3 +} diff --git a/examples/en/shops/04-sell-and-buy-shop/menu.conf b/examples/en/shops/04-sell-and-buy-shop/menu.conf new file mode 100644 index 0000000..47f7360 --- /dev/null +++ b/examples/en/shops/04-sell-and-buy-shop/menu.conf @@ -0,0 +1,139 @@ +# Sell-and-Buy Shop - left-click buys, right-click sells +# Docs: https://abstractmenus.github.io/docs/en/examples/shops/sell-and-buy-shop +# Open: /ame_buysell +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Trader" +size: 3 + +activators { + command: "ame_buysell" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Wheat - left-click buys 16 for $20, right-click sells 16 for $15. + { + slot: 11 + material: WHEAT + count: 16 + name: "&6Wheat &8(stack of 16)" + lore: [ + "&7Buy: &a$20" + "&7Sell: &a$15" + "" + "&aLeft-click - buy" + "&cRight-click - sell" + ] + click { + left { + rules { money: 20 } + actions { + takeMoney: 20 + itemAdd { material: WHEAT, count: 16 } + sound: ${successSound} + message: "&aBought 16 wheat for $20." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: WHEAT, count: 16 }] } + actions { + itemRemove: [{ material: WHEAT, count: 16 }] + giveMoney: 15 + sound: ${successSound} + message: "&aSold 16 wheat for $15." + } + denyActions { + sound: ${deniedSound} + message: "&cYou don't have 16 wheat to sell." + } + } + } + } + + # Iron - left/right with higher price tier + { + slot: 13 + material: IRON_INGOT + count: 8 + name: "&fIron Ingot &8(stack of 8)" + lore: [ + "&7Buy: &a$80" + "&7Sell: &a$60" + "" + "&aLeft-click - buy" + "&cRight-click - sell" + ] + click { + left { + rules { money: 80 } + actions { + takeMoney: 80 + itemAdd { material: IRON_INGOT, count: 8 } + sound: ${successSound} + message: "&aBought 8 iron ingots for $80." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: IRON_INGOT, count: 8 }] } + actions { + itemRemove: [{ material: IRON_INGOT, count: 8 }] + giveMoney: 60 + sound: ${successSound} + message: "&aSold 8 iron ingots for $60." + } + denyActions { + sound: ${deniedSound} + message: "&cYou don't have 8 iron ingots to sell." + } + } + } + } + + # Diamond - bigger spread for valuables + { + slot: 15 + material: DIAMOND + name: "&bDiamond" + lore: [ + "&7Buy: &a$500" + "&7Sell: &a$350" + "" + "&aLeft-click - buy" + "&cRight-click - sell" + ] + click { + left { + rules { money: 500 } + actions { + takeMoney: 500 + itemAdd { material: DIAMOND } + sound: ${successSound} + message: "&aBought 1 diamond for $500." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: DIAMOND, count: 1 }] } + actions { + itemRemove: [{ material: DIAMOND, count: 1 }] + giveMoney: 350 + sound: ${successSound} + message: "&aSold 1 diamond for $350." + } + denyActions { + sound: ${deniedSound} + message: "&cYou don't have a diamond to sell." + } + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/shops/04-sell-and-buy-shop/meta.json b/examples/en/shops/04-sell-and-buy-shop/meta.json new file mode 100644 index 0000000..d1a19d8 --- /dev/null +++ b/examples/en/shops/04-sell-and-buy-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Sell & Buy Shop", + "category": "shops", + "level": "intermediate", + "features": ["click-types-left-right", "money-rule", "inventoryItems-rule", "takeMoney", "giveMoney", "itemRemove", "asymmetric-prices"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_buysell", + "order": 4 +} diff --git a/examples/en/shops/05-npc-vendor/menu.conf b/examples/en/shops/05-npc-vendor/menu.conf new file mode 100644 index 0000000..035697d --- /dev/null +++ b/examples/en/shops/05-npc-vendor/menu.conf @@ -0,0 +1,75 @@ +# NPC Vendor - shop opened by clicking a Citizens NPC +# Docs: https://abstractmenus.github.io/docs/en/examples/shops/npc-vendor +# Open: right-click the Citizens NPC with id 1 (replace with your NPC's id) +# Requires: Citizens, Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Trader %activator_npc_name%" +size: 3 + +activators { + clickNPC: [1] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: EMERALD + name: "&aWelcome, traveler" + lore: ["&7Buy goods from %activator_npc_name%."] + } + + { + slot: 11 + material: BREAD + count: 8 + name: "&6Bread x8" + lore: ["&ePrice: &a$30"] + click { + rules { money: 30 } + actions { + takeMoney: 30 + itemAdd { material: BREAD, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 13 + material: COOKED_BEEF + count: 4 + name: "&6Steak x4" + lore: ["&ePrice: &a$50"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: COOKED_BEEF, count: 4 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 15 + material: GOLDEN_APPLE + name: "&6Golden Apple" + lore: ["&ePrice: &a$200"] + click { + rules { money: 200 } + actions { + takeMoney: 200 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/shops/05-npc-vendor/meta.json b/examples/en/shops/05-npc-vendor/meta.json new file mode 100644 index 0000000..3e8d738 --- /dev/null +++ b/examples/en/shops/05-npc-vendor/meta.json @@ -0,0 +1,12 @@ +{ + "title": "NPC Vendor", + "category": "shops", + "level": "intermediate", + "features": ["clickNPC-activator", "Citizens-integration", "%activator_npc_*%-placeholders", "money-rule"], + "dependencies": ["Citizens", "Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(right-click NPC)", + "order": 5 +} diff --git a/examples/en/snippets/01-rule-permission/menu.conf b/examples/en/snippets/01-rule-permission/menu.conf new file mode 100644 index 0000000..3bced52 --- /dev/null +++ b/examples/en/snippets/01-rule-permission/menu.conf @@ -0,0 +1,28 @@ +# Snippet: permission rule +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/rule-permission +# Open: /ame_snip_perm + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Permission Rule" +size: 1 +activators { command: "ame_snip_perm" } + +items: [ + { + slot: 4 + material: DIAMOND + name: "&bAdmin Reward" + lore: ["&7Click - admins only."] + click { + rules { permission: "abstractmenus.admin" } + actions { + itemAdd { material: DIAMOND } + sound: ${successSound} + message: "&aAdmin diamond claimed." + } + denyActions: ${denyNoPerm} + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/01-rule-permission/meta.json b/examples/en/snippets/01-rule-permission/meta.json new file mode 100644 index 0000000..1e28f14 --- /dev/null +++ b/examples/en/snippets/01-rule-permission/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rule: permission", + "category": "snippets", + "level": "beginner", + "features": ["permission-rule", "denyActions-substitution"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_perm", + "order": 1 +} diff --git a/examples/en/snippets/02-rule-money/menu.conf b/examples/en/snippets/02-rule-money/menu.conf new file mode 100644 index 0000000..5c5c3f9 --- /dev/null +++ b/examples/en/snippets/02-rule-money/menu.conf @@ -0,0 +1,28 @@ +# Snippet: money rule + takeMoney +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/rule-money +# Open: /ame_snip_money + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Money Rule" +size: 1 +activators { command: "ame_snip_money" } + +items: [ + { + slot: 4 + material: GOLDEN_APPLE + name: "&6Buy Golden Apple" + lore: ["&7Price: &a$50"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/02-rule-money/meta.json b/examples/en/snippets/02-rule-money/meta.json new file mode 100644 index 0000000..a17b1c3 --- /dev/null +++ b/examples/en/snippets/02-rule-money/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rule: money", + "category": "snippets", + "level": "beginner", + "features": ["money-rule", "takeMoney"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_money", + "order": 2 +} diff --git a/examples/en/snippets/03-rule-chance/menu.conf b/examples/en/snippets/03-rule-chance/menu.conf new file mode 100644 index 0000000..7fe37b3 --- /dev/null +++ b/examples/en/snippets/03-rule-chance/menu.conf @@ -0,0 +1,22 @@ +# Snippet: chance rule for probabilistic display +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/rule-chance +# Open: /ame_snip_chance + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Chance Rule" +size: 1 +activators { command: "ame_snip_chance" } + +items: [ + # 30% chance to render this rare item at slot 4 + { + slot: 4 + material: NETHER_STAR + name: "&dRare Star" + lore: ["&730% chance to appear."] + rules { chance: 30 } + } + # Fallback: 70% chance the slot stays empty (no fallback item, slot just blank) + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/03-rule-chance/meta.json b/examples/en/snippets/03-rule-chance/meta.json new file mode 100644 index 0000000..24d0998 --- /dev/null +++ b/examples/en/snippets/03-rule-chance/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rule: chance", + "category": "snippets", + "level": "beginner", + "features": ["chance-rule"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_chance", + "order": 3 +} diff --git a/examples/en/snippets/04-rule-if-placeholder/menu.conf b/examples/en/snippets/04-rule-if-placeholder/menu.conf new file mode 100644 index 0000000..58efae3 --- /dev/null +++ b/examples/en/snippets/04-rule-if-placeholder/menu.conf @@ -0,0 +1,38 @@ +# Snippet: if rule with placeholder expression +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/rule-if-placeholder +# Open: /ame_snip_if + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6If Rule" +size: 1 +activators { command: "ame_snip_if" } + +items: [ + # Visible only if the player has level 10+. Compares a placeholder via expression. + { + slot: 4 + material: ENCHANTED_BOOK + name: "&aLevel 10+ Reward" + lore: ["&7Visible to anyone level 10 or higher."] + rules { + if: "%player_level% >= 10" + } + click { + actions { + itemAdd { material: EXPERIENCE_BOTTLE, count: 4 } + sound: ${successSound} + } + } + } + + # Fallback message for low-level players + { + slot: 4 + material: BARRIER + name: "&cLevel too low" + lore: ["&7Reach level 10 to claim this reward."] + } + + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/04-rule-if-placeholder/meta.json b/examples/en/snippets/04-rule-if-placeholder/meta.json new file mode 100644 index 0000000..8e1d6b1 --- /dev/null +++ b/examples/en/snippets/04-rule-if-placeholder/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rule: if (placeholder expression)", + "category": "snippets", + "level": "intermediate", + "features": ["if-rule", "placeholder-comparison", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_if", + "order": 4 +} diff --git a/examples/en/snippets/05-rule-and-or/menu.conf b/examples/en/snippets/05-rule-and-or/menu.conf new file mode 100644 index 0000000..141adf1 --- /dev/null +++ b/examples/en/snippets/05-rule-and-or/menu.conf @@ -0,0 +1,42 @@ +# Snippet: and / or logical wrappers +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/rule-and-or +# Open: /ame_snip_andor + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Logical Rules" +size: 1 +activators { command: "ame_snip_andor" } + +items: [ + # AND - all sub-rules must match. (rules block defaults to AND, this is here for explicit demo.) + { + slot: 2 + material: DIAMOND_SWORD + name: "&bAdmin in CREATIVE" + lore: ["&7Need: admin permission AND creative mode."] + flags: HIDE_ATTRIBUTES + rules { + and { + permission: "abstractmenus.admin" + gamemode: CREATIVE + } + } + } + + # OR - at least one sub-rule matches. + { + slot: 6 + material: GOLDEN_APPLE + name: "&aVIP or rich" + lore: ["&7Need: VIP permission OR $1000."] + rules { + or { + permission: "abstractmenus.vip" + money: 1000 + } + } + } + + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/05-rule-and-or/meta.json b/examples/en/snippets/05-rule-and-or/meta.json new file mode 100644 index 0000000..1ff1214 --- /dev/null +++ b/examples/en/snippets/05-rule-and-or/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Rules: and / or", + "category": "snippets", + "level": "intermediate", + "features": ["and-wrapper", "or-wrapper", "logical-composition"], + "dependencies": ["LuckPerms", "Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_andor", + "order": 5 +} diff --git a/examples/en/snippets/06-action-give-item/menu.conf b/examples/en/snippets/06-action-give-item/menu.conf new file mode 100644 index 0000000..9150c8f --- /dev/null +++ b/examples/en/snippets/06-action-give-item/menu.conf @@ -0,0 +1,28 @@ +# Snippet: itemAdd action +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/action-give-item +# Open: /ame_snip_give + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6itemAdd Action" +size: 1 +activators { command: "ame_snip_give" } + +items: [ + { + slot: 4 + material: WOODEN_PICKAXE + name: "&aGive me a tool" + lore: ["&7Click to receive a wooden pickaxe."] + click { + actions { + itemAdd { + material: WOODEN_PICKAXE + name: "&aStarter Pickaxe" + } + sound: ${successSound} + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/06-action-give-item/meta.json b/examples/en/snippets/06-action-give-item/meta.json new file mode 100644 index 0000000..d0f2ab4 --- /dev/null +++ b/examples/en/snippets/06-action-give-item/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Action: itemAdd", + "category": "snippets", + "level": "beginner", + "features": ["itemAdd"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_give", + "order": 6 +} diff --git a/examples/en/snippets/07-action-bulk-delay/menu.conf b/examples/en/snippets/07-action-bulk-delay/menu.conf new file mode 100644 index 0000000..7f86c12 --- /dev/null +++ b/examples/en/snippets/07-action-bulk-delay/menu.conf @@ -0,0 +1,40 @@ +# Snippet: bulk + delay action wrappers +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/action-bulk-delay +# Open: /ame_snip_bulkdelay + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Bulk + Delay" +size: 1 +activators { command: "ame_snip_bulkdelay" } + +items: [ + { + slot: 4 + material: TNT + name: "&cMulti-step countdown" + lore: [ + "&7Click - get a sequence of messages" + "&7and a sound at the end." + ] + click { + actions { + # bulk = list of action groups, executed in order + bulk: [ + { message: "&e3..." } + { message: "&62..." } + { message: "&c1..." } + ] + # delay = run the wrapped actions block N ticks later + delay { + delay: 60 + actions { + sound: ${successSound} + message: "&aBoom!" + } + } + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/07-action-bulk-delay/meta.json b/examples/en/snippets/07-action-bulk-delay/meta.json new file mode 100644 index 0000000..73fc3b5 --- /dev/null +++ b/examples/en/snippets/07-action-bulk-delay/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Actions: bulk + delay", + "category": "snippets", + "level": "intermediate", + "features": ["bulk", "delay", "action-wrappers"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_bulkdelay", + "order": 7 +} diff --git a/examples/en/snippets/08-action-rand-actions/menu.conf b/examples/en/snippets/08-action-rand-actions/menu.conf new file mode 100644 index 0000000..8a49bb7 --- /dev/null +++ b/examples/en/snippets/08-action-rand-actions/menu.conf @@ -0,0 +1,45 @@ +# Snippet: randActions for one-of-N random outcome +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/action-rand-actions +# Open: /ame_snip_rand + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Random Actions" +size: 1 +activators { command: "ame_snip_rand" } + +items: [ + { + slot: 4 + material: ENDER_PEARL + name: "&5Lucky Roll" + lore: ["&7Click - random outcome from 4 options."] + click { + actions { + # Pick exactly one of the 4 entries at random and run its block. + randActions: [ + { + sound: ${successSound} + message: "&aJackpot! +10 XP" + giveXp: 10 + } + { + sound: ${successSound} + message: "&eDecent. +1 emerald" + itemAdd { material: EMERALD } + } + { + sound: ${clickSound} + message: "&7Meh. +5 cooked beef" + itemAdd { material: COOKED_BEEF, count: 5 } + } + { + sound: ${failSound} + message: "&cBetter luck next time." + } + ] + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/08-action-rand-actions/meta.json b/examples/en/snippets/08-action-rand-actions/meta.json new file mode 100644 index 0000000..1f34979 --- /dev/null +++ b/examples/en/snippets/08-action-rand-actions/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Action: randActions", + "category": "snippets", + "level": "intermediate", + "features": ["randActions", "weighted-random-via-repeat"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_rand", + "order": 8 +} diff --git a/examples/en/snippets/09-activator-clickitem/menu.conf b/examples/en/snippets/09-activator-clickitem/menu.conf new file mode 100644 index 0000000..9cf7e91 --- /dev/null +++ b/examples/en/snippets/09-activator-clickitem/menu.conf @@ -0,0 +1,33 @@ +# Snippet: clickItem activator - open menu by right-clicking an item in hand +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/activator-clickitem +# Open: hold a NETHER_STAR named "Menu Wand" and right-click it. +# (To get one for testing: /give @s minecraft:nether_star{display:{Name:'{"text":"Menu Wand"}'}}) +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Menu opened by item click" +size: 1 + +activators { + clickItem { + material: NETHER_STAR + name: "&6Menu Wand" + } +} + +items: [ + { + slot: 4 + material: BOOK + name: "&aIt worked" + lore: [ + "&7You opened this menu by right-clicking" + "&7a NETHER_STAR named &6Menu Wand&7 in your hand." + "" + "&7See activators docs for clickEntity, clickBlockType," + "&7clickNPC, regionJoin, and others." + ] + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/09-activator-clickitem/meta.json b/examples/en/snippets/09-activator-clickitem/meta.json new file mode 100644 index 0000000..7f6201f --- /dev/null +++ b/examples/en/snippets/09-activator-clickitem/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Activator: clickItem", + "category": "snippets", + "level": "intermediate", + "features": ["clickItem-activator", "no-command"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(right-click item)", + "order": 9 +} diff --git a/examples/en/snippets/10-template-substitution/menu.conf b/examples/en/snippets/10-template-substitution/menu.conf new file mode 100644 index 0000000..7e1aa8e --- /dev/null +++ b/examples/en/snippets/10-template-substitution/menu.conf @@ -0,0 +1,38 @@ +# Snippet: HOCON template substitution + override +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/template-substitution +# Open: /ame_snip_template + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Template Substitution" +size: 1 +activators { command: "ame_snip_template" } + +# A reusable item template defined at file scope. +prizeTile { + material: NETHER_STAR + lore: ["&7Click for a prize."] + click { + actions { + itemAdd { material: DIAMOND } + sound: ${successSound} + } + } +} + +items: [ + # Reference the template, override slot + name. The click block from prizeTile is preserved. + ${prizeTile} { + slot: 2 + name: "&aCommon Prize" + } + ${prizeTile} { + slot: 4 + name: "&6Rare Prize" + } + ${prizeTile} { + slot: 6 + name: "&dEpic Prize" + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/snippets/10-template-substitution/meta.json b/examples/en/snippets/10-template-substitution/meta.json new file mode 100644 index 0000000..b3c96e7 --- /dev/null +++ b/examples/en/snippets/10-template-substitution/meta.json @@ -0,0 +1,12 @@ +{ + "title": "HOCON template substitution", + "category": "snippets", + "level": "intermediate", + "features": ["template-substitution", "object-merge", "DRY-pattern"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_template", + "order": 10 +} diff --git a/examples/en/snippets/11-inventory-crafting/menu.conf b/examples/en/snippets/11-inventory-crafting/menu.conf new file mode 100644 index 0000000..92f0e60 --- /dev/null +++ b/examples/en/snippets/11-inventory-crafting/menu.conf @@ -0,0 +1,102 @@ +# Inventory Crafting - craft items by consuming ingredients from inventory +# Docs: https://abstractmenus.github.io/docs/en/examples/snippets/inventory-crafting +# Open: /ame_craft +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Hunter's Workbench" +size: 1 + +activators { + command: "ame_craft" +} + +# Recipe definitions kept at file level - the click block references them via ${...}. + +helmetRecipe { + output { + material: LEATHER_HELMET + name: "&aHunter's Hat" + enchantments { + durability: 2 + } + } + requirements: [ + { material: LEATHER, count: 5 } + ] +} + +chestplateRecipe { + output { + material: LEATHER_CHESTPLATE + name: "&aHunter's Jacket" + enchantments { + durability: 2 + thorns: 1 + } + } + requirements: [ + { material: LEATHER, count: 8 } + { material: STRING, count: 1 } + { material: IRON_INGOT, count: 1 } + ] +} + +items: [ + ${helmetRecipe.output} { + slot: 2 + flags: HIDE_ATTRIBUTES + lore: [ + "&7Required:" + "&7- Leather x5" + "" + "&aClick to craft" + ] + click { + rules { + inventoryItems: ${helmetRecipe.requirements} + } + actions { + itemRemove: ${helmetRecipe.requirements} + itemAdd: ${helmetRecipe.output} + sound: ${successSound} + message: "&aYou crafted a Hunter's Hat!" + } + denyActions: ${denyNotEnoughItems} + } + } + + ${chestplateRecipe.output} { + slot: 6 + flags: HIDE_ATTRIBUTES + lore: [ + "&7Required:" + "&7- Leather x8" + "&7- String x1" + "&7- Iron Ingot x1" + "" + "&aClick to craft" + ] + click { + rules { + inventoryItems: ${chestplateRecipe.requirements} + } + actions { + itemRemove: ${chestplateRecipe.requirements} + itemAdd: ${chestplateRecipe.output} + sound: ${successSound} + message: "&aYou crafted a Hunter's Jacket!" + } + denyActions: ${denyNotEnoughItems} + } + } + + ${buttonClose} { slot: 8 } +] + +# Local deny action for missing-ingredients case +denyNotEnoughItems { + sound: ${deniedSound} + message: "&cYou don't have enough materials." +} diff --git a/examples/en/snippets/11-inventory-crafting/meta.json b/examples/en/snippets/11-inventory-crafting/meta.json new file mode 100644 index 0000000..f059853 --- /dev/null +++ b/examples/en/snippets/11-inventory-crafting/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Inventory Crafting", + "category": "snippets", + "level": "intermediate", + "features": ["inventoryItems-rule", "itemRemove", "itemAdd", "template-substitution", "recipe-template"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_craft", + "order": 11 +} diff --git a/examples/en/state-and-vars/01-counter-toggle/menu.conf b/examples/en/state-and-vars/01-counter-toggle/menu.conf new file mode 100644 index 0000000..d23b74f --- /dev/null +++ b/examples/en/state-and-vars/01-counter-toggle/menu.conf @@ -0,0 +1,107 @@ +# Counter & Toggle - personal variable demo +# Docs: https://abstractmenus.github.io/docs/en/examples/state-and-vars/counter-toggle +# Open: /ame_counter +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Counter & Toggle" +size: 3 + +activators { + command: "ame_counter" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Counter section ========== + # Reset counter + { + slot: 10 + material: BARRIER + name: "&cReset counter" + lore: ["&7Click to clear the counter."] + click { + actions { + removeVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Decrement + { + slot: 11 + material: RED_WOOL + name: "&c-1" + click { + actions { + decVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Counter display - default 0 if var not set + { + slot: 12 + material: BOOK + name: "&6Counter: &e%varp_:ame_counter:0%" + lore: ["&7Your personal counter, persisted between sessions."] + } + + # Increment + { + slot: 13 + material: GREEN_WOOL + name: "&a+1" + click { + actions { + incVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # ========== Toggle section ========== + # Dual-item pattern: first item renders when var exists, + # second item renders when it doesn't. + + # Toggle ON state (visible only when var exists) + { + slot: 15 + material: LIME_DYE + name: "&aToggle: ON" + lore: ["&7Click to turn OFF."] + rules { existVarp: "ame_toggle" } + click { + actions { + removeVarp: "ame_toggle" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Toggle OFF state (fallback, no rules - renders when ON variant hides) + { + slot: 15 + material: GRAY_DYE + name: "&7Toggle: OFF" + lore: ["&7Click to turn ON."] + click { + actions { + setVarp: "ame_toggle::1" + sound: ${clickSound} + refreshMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/state-and-vars/01-counter-toggle/meta.json b/examples/en/state-and-vars/01-counter-toggle/meta.json new file mode 100644 index 0000000..64b7ffd --- /dev/null +++ b/examples/en/state-and-vars/01-counter-toggle/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Counter & Toggle", + "category": "state-and-vars", + "level": "intermediate", + "features": ["incVarp", "decVarp", "setVarp", "removeVarp", "existVarp", "refreshMenu", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_counter", + "order": 1 +} diff --git a/examples/en/state-and-vars/02-vote-cooldown/menu.conf b/examples/en/state-and-vars/02-vote-cooldown/menu.conf new file mode 100644 index 0000000..c3c49a0 --- /dev/null +++ b/examples/en/state-and-vars/02-vote-cooldown/menu.conf @@ -0,0 +1,79 @@ +# Vote Cooldown - global vote counter + per-player 24h cooldown +# Docs: https://abstractmenus.github.io/docs/en/examples/state-and-vars/vote-cooldown +# Open: /ame_vote +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Daily Vote" +size: 3 +updateInterval: 20 + +activators { + command: "ame_vote" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Global vote count - shows how many total votes the server has received. + { + slot: 11 + material: BEACON + name: "&6Total Server Votes" + lore: ["", "&e%var_:ame_vote_total:0%", "", "&8(All-time count, all players)"] + } + + # ========== Vote button - dual variant ========== + # ON COOLDOWN variant: per-player temporal var exists, can't vote yet. + { + slot: 13 + material: GRAY_DYE + name: "&7Vote (on cooldown)" + lore: [ + "&7You've already voted recently." + "" + "&cAvailable in &e%varpt_:ame_vote_cd%" + ] + rules { existVarp: "ame_vote_cd" } + } + # READY variant: no cooldown, click to vote. + { + slot: 13 + material: LIME_DYE + name: "&aVote!" + lore: [ + "&7Click to vote for the server." + "&7You can vote once every 24 hours." + ] + click { + actions { + # Increment the global vote counter. + incVar: "ame_vote_total" + # Set per-player cooldown that auto-expires after 1 day. + setVarp: "ame_vote_cd::1::1d" + sound: ${successSound} + message: "&aThanks for voting! Come back in 24 hours." + refreshMenu: true + } + } + } + + # Reward earned info + { + slot: 15 + material: DIAMOND + name: "&bVoting rewards" + lore: [ + "&7Each vote gives:" + "&7- &f$100" + "&7- &f1 vote token" + "&7- &fchance for diamonds" + "" + "&8Configure in your vote handler" + ] + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/state-and-vars/02-vote-cooldown/meta.json b/examples/en/state-and-vars/02-vote-cooldown/meta.json new file mode 100644 index 0000000..d3c979b --- /dev/null +++ b/examples/en/state-and-vars/02-vote-cooldown/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Vote Cooldown", + "category": "state-and-vars", + "level": "intermediate", + "features": ["incVar-global", "setVarp-temporal", "existVarp", "varpt-placeholder", "var-placeholder", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_vote", + "order": 2 +} diff --git a/examples/en/state-and-vars/03-global-stats-board/menu.conf b/examples/en/state-and-vars/03-global-stats-board/menu.conf new file mode 100644 index 0000000..92de577 --- /dev/null +++ b/examples/en/state-and-vars/03-global-stats-board/menu.conf @@ -0,0 +1,73 @@ +# Global Stats Board - live display of player and server stats +# Docs: https://abstractmenus.github.io/docs/en/examples/state-and-vars/global-stats-board +# Open: /ame_stats +# Requires: PlaceholderAPI + statistic expansion + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6&l%player_name%'s Stats" +size: 4 +updateInterval: 20 + +activators { + command: "ame_stats" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "27-35" } + + # ========== Player section (row 1) ========== + { + slot: 10 + texture: "ebfe9b7af68dc1264a6fc969f3d7b08e50e61e6ee91cea83daabd18820f0ef" + name: "&6Player Kills" + lore: ["", "&e%statistic_player_kills%"] + } + { + slot: 12 + texture: "1ae3855f952cd4a03c148a946e3f812a5955ad35cbcb52627ea4acd47d3081" + name: "&6Deaths" + lore: ["", "&c%statistic_deaths%"] + } + { + slot: 14 + texture: "f67b27fb7e29ec98e1cd4a8f8466856d9ef3f2e9fbd9aed6311f8abe54b6ab2" + name: "&6Mob Kills" + lore: ["", "&e%statistic_mob_kills%"] + } + { + slot: 16 + texture: "f32c6171532a2a87f0eeb28edd010833f33f0ae6841a524e1b5200a35d385050" + name: "&6Hours Played" + lore: ["", "&e%statistic_hours_played%h"] + } + + # ========== Server section (row 2) ========== + { + slot: 19 + material: BEACON + name: "&aOnline Now" + lore: ["", "&e%server_online%&7 / &e%server_max_players%"] + } + { + slot: 21 + material: CLOCK + name: "&aServer Time" + lore: ["", "&e%server_time_HH:mm:ss%"] + } + { + slot: 23 + material: ENDER_CHEST + name: "&aTPS" + lore: ["", "&e%server_tps_1%&7, &e%server_tps_5%&7, &e%server_tps_15%"] + } + { + slot: 25 + material: COMPASS + name: "&aWorld" + lore: ["", "&e%player_world%"] + } + + ${buttonClose} { slot: 31 } +] diff --git a/examples/en/state-and-vars/03-global-stats-board/meta.json b/examples/en/state-and-vars/03-global-stats-board/meta.json new file mode 100644 index 0000000..e6434e3 --- /dev/null +++ b/examples/en/state-and-vars/03-global-stats-board/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Global Stats Board", + "category": "state-and-vars", + "level": "intermediate", + "features": ["updateInterval", "placeholders", "statistic-expansion", "server-expansion", "PLAYER_HEAD-texture"], + "dependencies": ["PlaceholderAPI"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_stats", + "order": 3 +} diff --git a/examples/en/state-and-vars/04-bungee-status/menu.conf b/examples/en/state-and-vars/04-bungee-status/menu.conf new file mode 100644 index 0000000..9c4e7d6 --- /dev/null +++ b/examples/en/state-and-vars/04-bungee-status/menu.conf @@ -0,0 +1,90 @@ +# Bungee Status - server picker showing online counts via BungeeCord +# Docs: https://abstractmenus.github.io/docs/en/examples/state-and-vars/bungee-status +# Open: /ame_servers +# Requires: BungeeCord proxy (or Velocity) with named backends + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Server Selector" +size: 3 +updateInterval: 40 + +activators { + command: "ame_servers" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Lobby + { + slot: 10 + material: BOOKSHELF + name: "&e&lLobby" + lore: [ + "&7Main hub for picking modes." + "" + "&7Online: &e%bungee_lobby%" + "" + "&a> Click to connect" + ] + glow: true + click { + bungeeConnect: "lobby" + } + } + + # Survival + { + slot: 12 + material: GRASS_BLOCK + name: "&e&lSurvival" + lore: [ + "&7Vanilla-style survival world." + "" + "&7Online: &e%bungee_survival%" + "" + "&a> Click to connect" + ] + click { + bungeeConnect: "survival" + } + } + + # SkyWars + { + slot: 14 + material: ENDER_EYE + name: "&e&lSky Wars" + lore: [ + "&7PvP minigame on islands." + "" + "&7Online: &e%bungee_sw%" + "" + "&a> Click to connect" + ] + click { + bungeeConnect: "sw" + } + } + + # Build Battle + { + slot: 16 + material: BRICKS + name: "&e&lBuild Battle" + lore: [ + "&7Themed build competitions." + "" + "&7Online: &e%bungee_bb%" + "" + "&a> Click to connect" + ] + click { + bungeeConnect: "bb" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/state-and-vars/04-bungee-status/meta.json b/examples/en/state-and-vars/04-bungee-status/meta.json new file mode 100644 index 0000000..61a3b58 --- /dev/null +++ b/examples/en/state-and-vars/04-bungee-status/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Bungee Status", + "category": "state-and-vars", + "level": "intermediate", + "features": ["bungeeConnect", "bungee-placeholders", "updateInterval", "glow"], + "dependencies": ["BungeeCord"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_servers", + "order": 4 +} diff --git a/examples/en/state-and-vars/05-item-enhancer/menu.conf b/examples/en/state-and-vars/05-item-enhancer/menu.conf new file mode 100644 index 0000000..18a81d3 --- /dev/null +++ b/examples/en/state-and-vars/05-item-enhancer/menu.conf @@ -0,0 +1,101 @@ +# Item Enhancer - drag-and-drop with conditional transformation via oneof rule +# Docs: https://abstractmenus.github.io/docs/en/examples/state-and-vars/item-enhancer +# Open: /ame_enhancer +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Item Enhancer" +size: 1 + +activators { + command: "ame_enhancer" +} + +# Slots 2 (input) and 6 (result) are draggable. Slot 4 is the trigger button. +draggable: [ + "--x---x--" +] + +# Result-slot placeholder shown when the menu opens or the input is invalid. +resultStub { + slot: 6 + material: WHITE_STAINED_GLASS_PANE + name: "&7Result" +} + +# Reusable bits +inputSlotRule { slot: 2 } +outputAction { slot: 6, count: "%changed_item_amount%" } + +# The transformation table: each entry pairs a "placed input matches X" rule +# with an action that produces output Y. `oneof` short-circuits on the first +# match - so the first matching transformation wins. +enhanceRules { + oneof: [ + { + placedItem: ${inputSlotRule} { material: STONE } + actions { placeItem: ${outputAction} { material: COAL_ORE } } + } + { + placedItem: ${inputSlotRule} { material: APPLE } + actions { placeItem: ${outputAction} { material: GOLDEN_APPLE } } + } + { + placedItem: ${inputSlotRule} { material: WOODEN_SWORD } + actions { placeItem: ${outputAction} { material: DIAMOND_SWORD } } + } + { + placedItem: ${inputSlotRule} { material: WOODEN_AXE } + actions { placeItem: ${outputAction} { material: DIAMOND_AXE } } + } + ] +} + +items: [ + # Decorative panes around the draggable slots + { + slot: [ "xx-x-x-x-" ] + material: BLACK_STAINED_GLASS_PANE + name: " " + } + + # Default result-slot placeholder + ${resultStub} + + # Recipes info card + { + slot: 8 + material: NAME_TAG + name: "&aRecipes" + lore: [ + "&fStone &7-> &bCoal Ore" + "&fApple &7-> &bGolden Apple" + "&fWooden Sword &7-> &bDiamond Sword" + "&fWooden Axe &7-> &bDiamond Axe" + ] + } + + # The "Enhance!" button + { + slot: 4 + material: NETHER_STAR + name: "&bEnhance!" + click { + rules: ${enhanceRules} + actions { + removePlaced: 2 + sound: ${successSound} + } + denyActions { + rules { + placedItem { slot: 6, material: AIR } + } + actions { + setButton: ${resultStub} + sound: ${deniedSound} + } + } + } + } +] diff --git a/examples/en/state-and-vars/05-item-enhancer/meta.json b/examples/en/state-and-vars/05-item-enhancer/meta.json new file mode 100644 index 0000000..2137c60 --- /dev/null +++ b/examples/en/state-and-vars/05-item-enhancer/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Item Enhancer", + "category": "state-and-vars", + "level": "advanced", + "features": ["draggable-slots", "placedItem-rule", "placeItem", "removePlaced", "setButton", "oneof-rule", "conditional-transformation"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_enhancer", + "order": 5 +} diff --git a/examples/en/world-integrations/01-region-welcome/menu.conf b/examples/en/world-integrations/01-region-welcome/menu.conf new file mode 100644 index 0000000..13cdac3 --- /dev/null +++ b/examples/en/world-integrations/01-region-welcome/menu.conf @@ -0,0 +1,61 @@ +# Region Welcome - greet players who enter a WorldGuard region +# Docs: https://abstractmenus.github.io/docs/en/examples/world-integrations/region-welcome +# Open: automatically on entering region "spawn" +# Requires: WorldGuard + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Welcome to Spawn" +size: 3 + +activators { + regionJoin: ["spawn"] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Welcome banner + { + slot: 4 + material: NETHER_STAR + name: "&6&lWelcome to MyTestServer" + lore: [ + "&7You've entered the &fSpawn&7 region." + "" + "&7Pick up where you left off." + ] + } + + # Quick links + { + slot: 11 + material: COMPASS + name: "&aOpen the main hub" + lore: ["&7Browse all available menus."] + click { + openMenu: "ame_hub" + } + } + { + slot: 13 + material: WRITTEN_BOOK + name: "&aServer rules" + lore: ["&7Read the rules before playing."] + click { + openMenu: "ame_rules" + } + } + { + slot: 15 + material: CHEST + name: "&aGet your starter kit" + lore: ["&7Free items for new players."] + click { + openMenu: "ame_kit" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/world-integrations/01-region-welcome/meta.json b/examples/en/world-integrations/01-region-welcome/meta.json new file mode 100644 index 0000000..f85c532 --- /dev/null +++ b/examples/en/world-integrations/01-region-welcome/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Region Welcome", + "category": "world-integrations", + "level": "intermediate", + "features": ["regionJoin", "openMenu", "WorldGuard-integration"], + "dependencies": ["WorldGuard"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(on region enter)", + "order": 1 +} diff --git a/examples/en/world-integrations/02-npc-click-info/menu.conf b/examples/en/world-integrations/02-npc-click-info/menu.conf new file mode 100644 index 0000000..d5ff347 --- /dev/null +++ b/examples/en/world-integrations/02-npc-click-info/menu.conf @@ -0,0 +1,73 @@ +# NPC Click Info - info menu about a Citizens NPC the player clicked +# Docs: https://abstractmenus.github.io/docs/en/examples/world-integrations/npc-click-info +# Open: right-click any Citizens NPC with id 1, 2, or 3 +# Requires: Citizens + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6NPC: %activator_npc_name%" +size: 3 + +activators { + clickNPC: [1, 2, 3] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Header pulls from NPC context + { + slot: 4 + material: PLAYER_HEAD + skullOwner: "%activator_npc_name%" + name: "&6%activator_npc_name%" + lore: [ + "&7Citizens NPC #&f%activator_npc_id%" + "&7World: &f%activator_npc_world%" + "" + "&7Click any tile below for an action." + ] + } + + # Generic actions any NPC click can offer + { + slot: 11 + material: PAPER + name: "&aGreet" + lore: ["&7Make this NPC say hi."] + click { + actions { + message: "&e[%activator_npc_name%] &fHello, %player_name%!" + sound: ${clickSound} + } + } + } + { + slot: 13 + material: COMPASS + name: "&aShow location" + lore: ["&7Print this NPC's coordinates."] + click { + actions { + message: "&e[%activator_npc_name%] &fI'm in %activator_npc_world% at NPC id %activator_npc_id%." + sound: ${clickSound} + } + } + } + { + slot: 15 + material: EMERALD + name: "&aTalk to vendor" + lore: ["&7Open this NPC's shop, if it has one."] + click { + actions { + # In a real setup you'd route specific NPCs to specific shop menus. + # The demo just opens the generic vendor shop. + openMenu: "ame_shop" + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/en/world-integrations/02-npc-click-info/meta.json b/examples/en/world-integrations/02-npc-click-info/meta.json new file mode 100644 index 0000000..4d660b6 --- /dev/null +++ b/examples/en/world-integrations/02-npc-click-info/meta.json @@ -0,0 +1,12 @@ +{ + "title": "NPC Click Info", + "category": "world-integrations", + "level": "intermediate", + "features": ["clickNPC-multiple-ids", "%activator_npc_*%-placeholders", "skullOwner-from-placeholder", "openMenu-routing"], + "dependencies": ["Citizens"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(right-click NPC)", + "order": 2 +} diff --git a/examples/en/world-integrations/03-block-as-button/menu.conf b/examples/en/world-integrations/03-block-as-button/menu.conf new file mode 100644 index 0000000..498cccb --- /dev/null +++ b/examples/en/world-integrations/03-block-as-button/menu.conf @@ -0,0 +1,29 @@ +# Block-as-Button - clickBlockType activator opens menu when player clicks a material +# Docs: https://abstractmenus.github.io/docs/en/examples/world-integrations/block-as-button +# Open: right-click any DIAMOND_BLOCK in the world +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&bDiamond Block clicked at %activator_block_x%, %activator_block_y%, %activator_block_z%" +size: 1 + +activators { + clickBlockType: ["DIAMOND_BLOCK"] +} + +items: [ + { + slot: 4 + material: DIAMOND + name: "&bWelcome" + lore: [ + "&7You clicked a diamond block in" + "&7world &f%activator_block_world%&7." + "" + "&7Activator placeholders give you the" + "&7clicked block's coords, world, type, etc." + ] + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/en/world-integrations/03-block-as-button/meta.json b/examples/en/world-integrations/03-block-as-button/meta.json new file mode 100644 index 0000000..aa6dade --- /dev/null +++ b/examples/en/world-integrations/03-block-as-button/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Block as Button", + "category": "world-integrations", + "level": "intermediate", + "features": ["clickBlockType-activator", "activator_block-placeholders", "no-command"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(right-click block)", + "order": 3 +} diff --git a/examples/ru/_shared/templates.conf b/examples/ru/_shared/templates.conf new file mode 100644 index 0000000..e36a69f --- /dev/null +++ b/examples/ru/_shared/templates.conf @@ -0,0 +1,82 @@ +# AbstractMenus example pack - shared templates (RU) +# Reusable HOCON snippets referenced from menus via ${blockName} substitution. +# Docs: https://abstractmenus.github.io/docs/ru/examples + +# Sound presets (Paper 1.21+ registry keys) +clickSound: "ui.button.click" +openSound: "block.chest.open" +closeSound: "block.chest.close" +successSound: "entity.experience_orb.pickup" +failSound: "block.note_block.bass" +deniedSound: "block.anvil.land" + +# Deny action snippets (use as: denyActions: ${denyNoMoney}) +denyNoMoney { + sound: ${deniedSound} + message: "&cУ вас недостаточно денег." +} +denyNoPerm { + sound: ${deniedSound} + message: "&cНет прав." +} +denyCooldown { + sound: ${deniedSound} + message: "&cВы на кулдауне. Попробуйте позже." +} + +# Border items (use as: ${borderBlack} { slot: "0-8" }) +borderBlack { + material: BLACK_STAINED_GLASS_PANE + name: " " +} +borderGray { + material: GRAY_STAINED_GLASS_PANE + name: " " +} +borderBlue { + material: BLUE_STAINED_GLASS_PANE + name: " " +} + +# Navigation buttons (use as: ${buttonClose} { slot: 22 }) +buttonClose { + material: BARRIER + name: "&cЗакрыть" + click { + closeMenu: true + } +} +buttonBack { + material: ARROW + name: "&eНазад" + lore: ["&7Вернуться в предыдущее меню"] + click { + closeMenu: true + } +} +buttonNext { + material: ARROW + name: "&eСледующая страница" + click { + pageNext: 1 + } +} +buttonPrev { + material: ARROW + name: "&eПредыдущая страница" + click { + pagePrev: 1 + } +} + +# Rule shortcut snippets (use as: rules: ${rulesAdmin}) +rulesAdmin { permission: "abstractmenus.admin" } +rulesVip { permission: "abstractmenus.vip" } +rulesDonator { permission: "abstractmenus.donator" } + +# Common decorative header (use as: ${commonHeader} { slot: 4 }) +commonHeader { + material: NETHER_STAR + name: "&6&lМеню примера" + lore: ["&7Добро пожаловать в пак примеров AbstractMenus"] +} diff --git a/examples/ru/admin-tools/01-online-players/menu.conf b/examples/ru/admin-tools/01-online-players/menu.conf new file mode 100644 index 0000000..262b2a0 --- /dev/null +++ b/examples/ru/admin-tools/01-online-players/menu.conf @@ -0,0 +1,88 @@ +# Онлайн-игроки - сгенерированное меню-каталог онлайн-игроков с админскими действиями +# Docs: https://abstractmenus.github.io/docs/ru/examples/admin-tools/online-players +# Open: /ame_online +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Онлайн-игроки (страница %page% / %pages_count%)" +size: 6 + +# Ограничиваем доступ - только для админов. +rules: ${rulesAdmin} + +activators { + command: "ame_online" +} + +# Подтягиваем онлайн-игроков как записи каталога. Каждая запись даёт плейсхолдеры %ctg_player_*%. +catalog { + type: PLAYERS +} + +# Сетка 7 в ширину из голов игроков с рамкой. 4 ряда по 7 = 28 голов на странице. +matrix { + cells: [ + "_________" + "_xxxxxxx_" + "_xxxxxxx_" + "_xxxxxxx_" + "_xxxxxxx_" + "_________" + ] + + templates { + "x" { + material: PLAYER_HEAD + skullOwner: "%ctg_player_name%" + name: "&e%ctg_player_name%" + lore: [ + "&7Здоровье: &c%ctg_player_health%" + "&7Мир: &b%ctg_player_world%" + "" + "&aЛКМ - телепорт к игроку" + "&cShift+ЛКМ - кикнуть игрока" + ] + click { + left { + actions { + command { + console: "tp %player_name% %ctg_player_name%" + } + sound: ${clickSound} + closeMenu: true + } + } + shift_left { + actions { + command { + console: "kick %ctg_player_name% &cКикнут админом" + } + sound: ${successSound} + refreshMenu: true + message: "&aКикнут &e%ctg_player_name%" + } + } + } + } + } +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "45-53" } + + # Кнопки пагинации - показываются только когда страниц больше одной. + ${buttonPrev} { slot: 48 } + ${buttonNext} { slot: 50 } + + # Счётчик страниц (только для информации) + { + slot: 49 + material: PAPER + name: "&eСтраница %page% / %pages_count%" + lore: ["&7Всего онлайн: &e%pages_total%"] + } + + ${buttonClose} { slot: 53 } +] diff --git a/examples/ru/admin-tools/01-online-players/meta.json b/examples/ru/admin-tools/01-online-players/meta.json new file mode 100644 index 0000000..5e4c08f --- /dev/null +++ b/examples/ru/admin-tools/01-online-players/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Онлайн-игроки", + "category": "admin-tools", + "level": "advanced", + "features": ["GeneratedMenu", "PLAYERS-catalog", "matrix-cells", "matrix-templates", "ctg-placeholders", "click-types-left-shift", "command-action", "pageNext-pagePrev"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_online", + "order": 1 +} diff --git a/examples/ru/admin-tools/02-quick-mod/menu.conf b/examples/ru/admin-tools/02-quick-mod/menu.conf new file mode 100644 index 0000000..1dda9bb --- /dev/null +++ b/examples/ru/admin-tools/02-quick-mod/menu.conf @@ -0,0 +1,110 @@ +# Quick Mod - меню для админов с частыми модераторскими командами +# Docs: https://abstractmenus.github.io/docs/ru/examples/admin-tools/quick-mod +# Open: /ame_mod (только для админов) +# Requires: любой плагин с командами /kick /ban /mute /gamemode + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&cБыстрые инструменты модератора" +size: 3 + +# Доступ на уровне меню - не-админы вообще не смогут его открыть. +rules: ${rulesAdmin} + +activators { + command: "ame_mod" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: NETHER_STAR + name: "&c&lБыстрые инструменты модератора" + lore: ["&7Частые модераторские действия.", "&8Только для админов."] + } + + # ========== Переключение режимов игры ========== + { + slot: 10 + material: GRASS_BLOCK + name: "&aВыживание" + lore: ["&7Переключить себя в режим выживания."] + click { + actions { + setGamemode: "SURVIVAL" + sound: ${clickSound} + message: "&aРежим: SURVIVAL" + } + } + } + { + slot: 11 + material: DIAMOND_PICKAXE + name: "&aТворчество" + flags: HIDE_ATTRIBUTES + lore: ["&7Переключить себя в творческий режим."] + click { + actions { + setGamemode: "CREATIVE" + sound: ${clickSound} + message: "&aРежим: CREATIVE" + } + } + } + { + slot: 12 + material: ENDER_EYE + name: "&aНаблюдатель" + lore: ["&7Переключить себя в режим наблюдателя."] + click { + actions { + setGamemode: "SPECTATOR" + sound: ${clickSound} + message: "&aРежим: SPECTATOR" + } + } + } + + # ========== Браузер игроков ========== + { + slot: 14 + material: PLAYER_HEAD + name: "&aСписок онлайн-игроков" + lore: ["&7Открыть меню онлайн-игроков", "&7для кика и телепорта."] + click { openMenu: "ame_online" } + } + + # ========== Серверные действия ========== + { + slot: 15 + material: TNT + name: "&cОбъявление: рестарт сервера" + lore: ["&7Отправить предупреждение в чат."] + click { + actions { + broadcast: "&c[Админ] &fРестарт сервера через 5 минут. Сохраните прогресс." + sound: ${successSound} + } + } + } + { + slot: 16 + material: COMPASS + name: "&aТелепорт на спавн" + lore: ["&7Выполнить /spawn для себя."] + click { + actions { + command { + player: "spawn" + } + sound: ${clickSound} + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/admin-tools/02-quick-mod/meta.json b/examples/ru/admin-tools/02-quick-mod/meta.json new file mode 100644 index 0000000..d22388a --- /dev/null +++ b/examples/ru/admin-tools/02-quick-mod/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Quick Mod", + "category": "admin-tools", + "level": "intermediate", + "features": ["menu-level-rules", "setGamemode", "broadcast", "command-action", "openMenu-chain"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_mod", + "order": 2 +} diff --git a/examples/ru/casino-and-games/01-roulette/menu.conf b/examples/ru/casino-and-games/01-roulette/menu.conf new file mode 100644 index 0000000..2dd7c93 --- /dev/null +++ b/examples/ru/casino-and-games/01-roulette/menu.conf @@ -0,0 +1,61 @@ +# Рулетка - анимация замедления из 8 кадров + случайный приз через randActions +# Docs: https://abstractmenus.github.io/docs/ru/examples/casino-and-games/roulette +# Open: /ame_roulette +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Рулетка" +size: 1 + +activators { + command: "ame_roulette" +} + +# Статичные предметы, которые видны во всех кадрах. clear: false на кадрах оставляет их на месте. +items: [ + ${borderBlack} { slot: 0 } + ${borderBlack} { slot: 1 } + ${borderBlack} { slot: 2 } + ${borderBlack} { slot: 6 } + ${borderBlack} { slot: 7 } + ${buttonClose} { slot: 8 } +] + +# Случайный приз, выдаётся после завершения вращения. +onAnimEnd { + randActions: [ + { + sound: ${successSound} + itemAdd { material: DIAMOND, count: 1 } + message: "&bВы выиграли 1 алмаз!" + } + { + sound: ${successSound} + itemAdd { material: EMERALD, count: 2 } + message: "&aВы выиграли 2 изумруда!" + } + { + sound: ${successSound} + itemAdd { material: GOLD_INGOT, count: 4 } + message: "&6Вы выиграли 4 золотых слитка!" + } + { + sound: ${failSound} + message: "&7В следующий раз повезёт." + } + ] +} + +# Кадры анимации. У каждого clear: false, чтобы статичные предметы не пропадали. +# Задержки растут к концу - получается ощущение замедления. +frames: [ + { delay: 2, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: EMERALD, name: "&a?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: GOLD_INGOT, name: "&6?" } ] } + { delay: 2, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 4, clear: false, items: [ { slot: 4, material: EMERALD, name: "&a?" } ] } + { delay: 6, clear: false, items: [ { slot: 4, material: GOLD_INGOT, name: "&6?" } ] } + { delay: 10, clear: false, items: [ { slot: 4, material: DIAMOND, name: "&b?" } ] } + { delay: 20, clear: false, items: [ { slot: 4, material: NETHER_STAR, name: "&e?" } ] } +] diff --git a/examples/ru/casino-and-games/01-roulette/meta.json b/examples/ru/casino-and-games/01-roulette/meta.json new file mode 100644 index 0000000..a859263 --- /dev/null +++ b/examples/ru/casino-and-games/01-roulette/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Рулетка", + "category": "casino-and-games", + "level": "intermediate", + "features": ["frames", "AnimatedMenu", "onAnimEnd", "randActions", "static-items-with-frames"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_roulette", + "order": 1 +} diff --git a/examples/ru/casino-and-games/02-slot-machine/menu.conf b/examples/ru/casino-and-games/02-slot-machine/menu.conf new file mode 100644 index 0000000..62d0106 --- /dev/null +++ b/examples/ru/casino-and-games/02-slot-machine/menu.conf @@ -0,0 +1,82 @@ +# Слот-машина - 3 барабана с независимой анимацией и случайным результатом +# Docs: https://abstractmenus.github.io/docs/ru/examples/casino-and-games/slot-machine +# Open: /ame_slots +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Слот-машина" +size: 3 + +activators { + command: "ame_slots" +} + +# Статичные предметы - рамки, заголовок, подпись со стоимостью. Кадры рисуются поверх барабанов. +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: GOLD_INGOT + name: "&6Слот-машина" + lore: ["&7Стоимость прокрута: &a$50", "&7Соберите три одинаковых - получите крупный выигрыш."] + } + + ${buttonClose} { slot: 22 } +] + +# После последнего кадра randActions выдаёт приз. Четыре исхода соответствуют +# (примерно) тому, что игрок видел во время прокрута. +onAnimEnd { + randActions: [ + # 60% мимо + { sound: ${failSound}, message: "&7Не сошлось. -$50" } + { sound: ${failSound}, message: "&7Не сошлось. -$50" } + { sound: ${failSound}, message: "&7Не сошлось. -$50" } + # 30% небольшой выигрыш + { sound: ${successSound}, message: "&aТри вишни! +$80", giveMoney: 80 } + # 8% средний выигрыш + { sound: ${successSound}, message: "&6Три колокольчика! +$200", giveMoney: 200 } + # 2% джекпот + { sound: ${successSound}, message: "&dДЖЕКПОТ! Три семёрки! +$1000", giveMoney: 1000 } + ] +} + +# Три барабана в слотах 11, 13, 15. Каждый прокручивает 5 символов с замедлением. +# Кадры рисуют все три одновременно - визуально барабаны крутятся синхронно, +# для демо этого достаточно. Чтобы каждый барабан крутился отдельно, понадобились +# бы отдельные анимированные меню или код в расширении. +frames: [ + { delay: 2, clear: false, items: [ + { slot: 11, material: APPLE, name: "&c?" } + { slot: 13, material: GOLD_INGOT, name: "&6?" } + { slot: 15, material: DIAMOND, name: "&b?" } + ]} + { delay: 2, clear: false, items: [ + { slot: 11, material: GOLD_INGOT, name: "&6?" } + { slot: 13, material: DIAMOND, name: "&b?" } + { slot: 15, material: NETHER_STAR, name: "&e?" } + ]} + { delay: 2, clear: false, items: [ + { slot: 11, material: DIAMOND, name: "&b?" } + { slot: 13, material: NETHER_STAR, name: "&e?" } + { slot: 15, material: APPLE, name: "&c?" } + ]} + { delay: 4, clear: false, items: [ + { slot: 11, material: NETHER_STAR, name: "&e?" } + { slot: 13, material: APPLE, name: "&c?" } + { slot: 15, material: GOLD_INGOT, name: "&6?" } + ]} + { delay: 8, clear: false, items: [ + { slot: 11, material: APPLE, name: "&c?" } + { slot: 13, material: APPLE, name: "&c?" } + { slot: 15, material: APPLE, name: "&c?" } + ]} + { delay: 20, clear: false, items: [ + { slot: 11, material: NETHER_STAR, name: "&eИТОГ" } + { slot: 13, material: NETHER_STAR, name: "&eИТОГ" } + { slot: 15, material: NETHER_STAR, name: "&eИТОГ" } + ]} +] diff --git a/examples/ru/casino-and-games/02-slot-machine/meta.json b/examples/ru/casino-and-games/02-slot-machine/meta.json new file mode 100644 index 0000000..7406698 --- /dev/null +++ b/examples/ru/casino-and-games/02-slot-machine/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Слот-машина", + "category": "casino-and-games", + "level": "intermediate", + "features": ["frames", "AnimatedMenu", "onAnimEnd", "randActions-weighted", "multi-reel"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_slots", + "order": 2 +} diff --git a/examples/ru/casino-and-games/03-lucky-chest/menu.conf b/examples/ru/casino-and-games/03-lucky-chest/menu.conf new file mode 100644 index 0000000..53794e5 --- /dev/null +++ b/examples/ru/casino-and-games/03-lucky-chest/menu.conf @@ -0,0 +1,70 @@ +# Lucky Chest - правило chance + взвешенные randActions для уровней призов +# Docs: https://abstractmenus.github.io/docs/ru/examples/casino-and-games/lucky-chest +# Open: /ame_chest +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Счастливый сундук" +size: 3 + +activators { + command: "ame_chest" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Информационная карточка + { + slot: 4 + material: PAPER + name: "&6Счастливый сундук" + lore: [ + "&7Заплатите $50, чтобы открыть сундук." + "&7Получите один из призов:" + "" + "&7- &fобычный: 12 жареной говядины &7(60%)" + "&7- &aнеобычный: 1 изумруд &7(30%)" + "&7- &6редкий: 1 зачарованное золотое яблоко &7(8%)" + "&7- &dмифический: 1 незеритовый слиток &7(2%)" + ] + } + + # Кнопка открытия + { + slot: 13 + material: CHEST + name: "&aОткрыть сундук &8($50)" + lore: ["&7Кликните, чтобы разыграть приз."] + click { + rules { money: 50 } + actions { + takeMoney: 50 + # Взвешенный рандом - чем чаще запись встречается, тем выше шанс. + randActions: [ + # 6 обычных записей = 60% + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ${successSound}, message: "&7Обычный: 12 жареной говядины." } + # 3 необычных записи = 30% + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aНеобычный: 1 изумруд!" } + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aНеобычный: 1 изумруд!" } + { itemAdd { material: EMERALD }, sound: ${successSound}, message: "&aНеобычный: 1 изумруд!" } + # ~1 редкий = ~8% (округляем до 1/12, поскольку всего 12 записей - для демо хватает) + { itemAdd { material: ENCHANTED_GOLDEN_APPLE }, sound: ${successSound}, message: "&6Редкий! Зачарованное золотое яблоко!" } + # ~1 мифический = ~8% (целились в 2%, но при 12 ячейках редкий и мифический сливаются) + { itemAdd { material: NETHERITE_INGOT }, sound: ${successSound}, message: "&dМИФИЧЕСКИЙ! Незеритовый слиток!" } + { itemAdd { material: NETHERITE_INGOT }, sound: ${successSound}, message: "&dМИФИЧЕСКИЙ! Незеритовый слиток!" } + ] + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/casino-and-games/03-lucky-chest/meta.json b/examples/ru/casino-and-games/03-lucky-chest/meta.json new file mode 100644 index 0000000..9197d7b --- /dev/null +++ b/examples/ru/casino-and-games/03-lucky-chest/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Счастливый сундук", + "category": "casino-and-games", + "level": "intermediate", + "features": ["randActions-weighted", "money-rule", "takeMoney", "tiered-rewards"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_chest", + "order": 3 +} diff --git a/examples/ru/casino-and-games/04-daily-lottery/menu.conf b/examples/ru/casino-and-games/04-daily-lottery/menu.conf new file mode 100644 index 0000000..82e20b9 --- /dev/null +++ b/examples/ru/casino-and-games/04-daily-lottery/menu.conf @@ -0,0 +1,90 @@ +# Ежедневная лотерея - глобальный накопитель джекпота + выплата по шансу +# Docs: https://abstractmenus.github.io/docs/ru/examples/casino-and-games/daily-lottery +# Open: /ame_lottery +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Ежедневная лотерея" +size: 3 +updateInterval: 40 + +activators { + command: "ame_lottery" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Отображение джекпота - читает глобальную переменную-накопитель + { + slot: 4 + material: NETHER_STAR + name: "&6&lТекущий джекпот" + lore: ["", "&e$%var_:ame_lottery_jackpot:0%", "", "&8Билет стоит $100. С каждых $100 в джекпот идёт $50."] + } + + # Кнопка покупки билета + { + slot: 13 + material: PAPER + name: "&aКупить билет &8($100)" + lore: [ + "&7Кликните, чтобы купить лотерейный билет." + "" + "&7Шанс выиграть весь джекпот - 1 к 50." + "" + "&7Если не повезло, $50 от стоимости билета" + "&7уйдут в призовой фонд." + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + # Шанс 1 к 50 выиграть джекпот. + bulk: [ + { + rules { chance: 2 } + actions { + # Выплачиваем победителю текущее значение джекпота. + # %var_:...% внутри actions - это подстановка переменной, передаёт её текущее значение. + giveMoney: "%var_:ame_lottery_jackpot:0%" + setVar: "ame_lottery_jackpot::0" + sound: ${successSound} + broadcast: "&6&lДЖЕКПОТ! &f%player_name% &aвыиграл $%var_:ame_lottery_jackpot:0% в лотерее!" + message: "&6&lВы выиграли джекпот!" + } + denyActions { + # Не выиграли - $50 от стоимости билета уходят в джекпот. + incVar { name: "ame_lottery_jackpot", amount: 50 } + sound: ${failSound} + message: "&7Не выиграли. $50 ушли в джекпот." + } + } + ] + refreshMenu: true + } + denyActions: ${denyNoMoney} + } + } + + # Сброс (только для админов) - на случай, если для тестов нужно очистить состояние меню + { + slot: 16 + material: BARRIER + name: "&cАдмин: сбросить джекпот в 0" + lore: ["&8Только для админов. Для тестов."] + rules: ${rulesAdmin} + click { + actions { + setVar: "ame_lottery_jackpot::0" + sound: ${clickSound} + message: "&aДжекпот сброшен." + refreshMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/casino-and-games/04-daily-lottery/meta.json b/examples/ru/casino-and-games/04-daily-lottery/meta.json new file mode 100644 index 0000000..d68cb0b --- /dev/null +++ b/examples/ru/casino-and-games/04-daily-lottery/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Ежедневная лотерея", + "category": "casino-and-games", + "level": "advanced", + "features": ["global-var-jackpot", "incVar-with-amount", "setVar", "chance-rule", "broadcast", "money-rule"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_lottery", + "order": 4 +} diff --git a/examples/ru/casino-and-games/05-enchanter/menu.conf b/examples/ru/casino-and-games/05-enchanter/menu.conf new file mode 100644 index 0000000..4e6d9a9 --- /dev/null +++ b/examples/ru/casino-and-games/05-enchanter/menu.conf @@ -0,0 +1,101 @@ +# Случайный зачарователь - drag-and-drop + лотерея зачарований через randActions +# Docs: https://abstractmenus.github.io/docs/ru/examples/casino-and-games/enchanter +# Open: /ame_enchanter +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Случайный зачарователь" +size: 3 + +activators { + command: "ame_enchanter" +} + +# Помечаем, какие слоты принимают drag-and-drop. Слот 11 (вход) и слот 15 (результат). +# `x` - drag-and-drop, `-` - обычный. +draggable: [ + "---------" + "--x---x--" + "---------" +] + +# Заглушка по умолчанию для слота результата, пока ничего не зачаровано. +resultStub { + slot: 15 + material: WHITE_STAINED_GLASS_PANE + name: "&7Результат" + lore: ["&8(пусто)"] +} + +# Шаблон действия: положить перетащенный предмет обратно в слот 15 с выбранным зачарованием. +# Плейсхолдеры %changed_item_*% ссылаются на то, что игрок положил в слот 11. +placeItemBase { + slot: 15 + serialized: "%changed_item_serialized%" + count: "%changed_item_amount%" +} + +# Полный блок click для кнопки зачарования. Вынесен отдельно, чтобы не загромождать список items. +enchantClick { + rules { + # Слот 11 НЕ должен быть пустым (префикс - инвертирует правило placedItem). + -placedItem { + slot: 11 + material: AIR + } + } + actions { + randActions: [ + { placeItem: ${placeItemBase} { enchantments { sharpness: 1, unbreaking: 2 } } } + { placeItem: ${placeItemBase} { enchantments { efficiency: 2 } } } + { placeItem: ${placeItemBase} { enchantments { fortune: 2, unbreaking: 1 } } } + { placeItem: ${placeItemBase} { enchantments { fire_aspect: 1 } } } + ] + # Убираем входной предмет из слота 11 (он "израсходован"). + removePlaced: 11 + sound: ${successSound} + } + # При клике с пустым входным слотом возвращаем заглушку в слот результата. + denyActions { + rules { + placedItem { + slot: 15 + material: AIR + } + } + actions { + setButton: ${resultStub} + sound: ${deniedSound} + } + } +} + +items: [ + # Декоративная рамка вокруг двух drag-and-drop слотов + { + slot: [ + "xxxxxxxxx" + "xx-xxx-xx" + "xxxxxxxxx" + ] + material: BLACK_STAINED_GLASS_PANE + name: " " + } + + # Заглушка пустого слота результата + ${resultStub} + + # Кнопка "Зачаровать!" в слоте 22 (нижний ряд, по центру) + { + slot: 22 + material: NETHER_STAR + name: "&bЗачаровать!" + lore: [ + "&7Положите инструмент в левый слот," + "&7затем кликните сюда, чтобы навесить" + "&7случайное зачарование." + ] + click: ${enchantClick} + } +] diff --git a/examples/ru/casino-and-games/05-enchanter/meta.json b/examples/ru/casino-and-games/05-enchanter/meta.json new file mode 100644 index 0000000..89a8c24 --- /dev/null +++ b/examples/ru/casino-and-games/05-enchanter/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Случайный зачарователь", + "category": "casino-and-games", + "level": "advanced", + "features": ["draggable-slots", "placeItem", "placedItem-rule", "removePlaced", "setButton", "randActions", "changed_item-placeholders"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_enchanter", + "order": 5 +} diff --git a/examples/ru/cosmetics/01-tags/menu.conf b/examples/ru/cosmetics/01-tags/menu.conf new file mode 100644 index 0000000..fe75733 --- /dev/null +++ b/examples/ru/cosmetics/01-tags/menu.conf @@ -0,0 +1,123 @@ +# Выбор тега - теги-префиксы на основе LuckPerms meta +# Docs: https://abstractmenus.github.io/docs/ru/examples/cosmetics/tags +# Open: /ame_tags +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Выбери свой тег" +size: 3 + +activators { + command: "ame_tags" +} + +# Переиспользуемый обработчик клика для применения тега. Каждый тег ссылается на него с переопределённой строкой тега. +applyTag { + actions { + lpMetaSet { + metaList: [ + { key: "ame_tag", value: "%selected_tag%" } + ] + } + sound: ${successSound} + closeMenu: true + } +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Тег-сердце + { + slot: 11 + material: RED_DYE + name: "&c♥ Сердце" + lore: ["&7Розовое сердце рядом с твоим ником."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&c♥" }] + } + sound: ${successSound} + message: "&cТег установлен на &c♥" + closeMenu: true + } + } + } + + # Тег-звезда + { + slot: 12 + material: NETHER_STAR + name: "&e★ Звезда" + lore: ["&7Жёлтая звезда."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&e★" }] + } + sound: ${successSound} + message: "&eТег установлен на &e★" + closeMenu: true + } + } + } + + # Тег-корона - только для VIP + { + slot: 13 + material: GOLDEN_HELMET + name: "&6♛ Корона &8(VIP)" + lore: ["&7Королевская корона.", "", "&8Требуется ранг VIP"] + rules { permission: "abstractmenus.vip" } + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&6♛" }] + } + sound: ${successSound} + message: "&6Тег установлен на &6♛" + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + # Тег-череп + { + slot: 14 + material: SKELETON_SKULL + name: "&8☠ Череп" + lore: ["&7Жуткий череп."] + click { + actions { + lpMetaSet { + metaList: [{ key: "ame_tag", value: "&8☠" }] + } + sound: ${successSound} + message: "&8Тег установлен на &8☠" + closeMenu: true + } + } + } + + # Сброс / очистка тега + { + slot: 15 + material: BARRIER + name: "&7Очистить тег" + lore: ["&7Удалить текущий тег."] + click { + actions { + lpMetaRemove: ["ame_tag"] + sound: ${clickSound} + message: "&7Тег удалён." + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/cosmetics/01-tags/meta.json b/examples/ru/cosmetics/01-tags/meta.json new file mode 100644 index 0000000..db8f27e --- /dev/null +++ b/examples/ru/cosmetics/01-tags/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Теги", + "category": "cosmetics", + "level": "intermediate", + "features": ["lpMetaSet", "lpMetaRemove", "permission-rule", "VIP-tier"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_tags", + "order": 1 +} diff --git a/examples/ru/cosmetics/02-prefixes/menu.conf b/examples/ru/cosmetics/02-prefixes/menu.conf new file mode 100644 index 0000000..f73519e --- /dev/null +++ b/examples/ru/cosmetics/02-prefixes/menu.conf @@ -0,0 +1,96 @@ +# Префиксы - выбор префикса через группы LuckPerms +# Docs: https://abstractmenus.github.io/docs/ru/examples/cosmetics/prefixes +# Open: /ame_prefixes +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Выбери свой префикс" +size: 3 + +activators { + command: "ame_prefixes" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Каждый префикс - это группа LuckPerms. Клик добавляет игрока в эту группу + # (и удаляет из остальных, чтобы активным был только один префикс). + + { + slot: 11 + material: GREEN_WOOL + name: "&a[Member]" + lore: [ + "&7Стандартный префикс." + "&7Бесплатный для всех игроков." + ] + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_donor" } + { removeGroup: "ame_prefix_admin" } + { addGroup: "ame_prefix_member" } + ] + sound: ${successSound} + message: "&aПрефикс установлен: [Member]." + closeMenu: true + } + } + } + + { + slot: 13 + material: GOLD_INGOT + name: "&6[Donor]" + lore: [ + "&7Префикс донатера." + "&7Доступен для поддержавших проект." + "", + "&8Требуется право донатера" + ] + rules { permission: "abstractmenus.donator" } + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_admin" } + { addGroup: "ame_prefix_donor" } + ] + sound: ${successSound} + message: "&6Префикс установлен: [Donor]." + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + { + slot: 15 + material: NETHERITE_INGOT + name: "&c[Admin]" + lore: [ + "&7Префикс администратора." + "", + "&8Требуется право администратора" + ] + rules { permission: "abstractmenus.admin" } + click { + actions { + bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_donor" } + { addGroup: "ame_prefix_admin" } + ] + sound: ${successSound} + message: "&cПрефикс установлен: [Admin]." + closeMenu: true + } + } + denyActions: ${denyNoPerm} + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/cosmetics/02-prefixes/meta.json b/examples/ru/cosmetics/02-prefixes/meta.json new file mode 100644 index 0000000..0cba86b --- /dev/null +++ b/examples/ru/cosmetics/02-prefixes/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Префиксы", + "category": "cosmetics", + "level": "intermediate", + "features": ["addGroup", "removeGroup", "bulk-action", "permission-rule", "exclusive-selection"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_prefixes", + "order": 2 +} diff --git a/examples/ru/cosmetics/03-color-skins/menu.conf b/examples/ru/cosmetics/03-color-skins/menu.conf new file mode 100644 index 0000000..18a1e81 --- /dev/null +++ b/examples/ru/cosmetics/03-color-skins/menu.conf @@ -0,0 +1,100 @@ +# Цветные скины - выбор скина через действие setSkin +# Docs: https://abstractmenus.github.io/docs/ru/examples/cosmetics/color-skins +# Open: /ame_skins +# Requires: SkinsRestorer + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Выбор скина" +size: 3 + +activators { + command: "ame_skins" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: PLAYER_HEAD + name: "&6&lВыбери скин" + lore: ["&7Выбери цвет для своего персонажа."] + } + + # Четыре цветных варианта. Замени texture и signature на значения с + # https://mineskin.org для реальных скинов. Заглушки ниже HOCON примет, + # но SkinsRestorer отклонит их во время выполнения. + + { + slot: 10 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_GOLD" + name: "&6Золотой" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_GOLD" + signature: "REPLACE_WITH_SIGNATURE_GOLD" + } + sound: ${successSound} + message: "&aЗолотой скин применён. Перезайди, чтобы увидеть изменения." + } + } + } + + { + slot: 12 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_PURPLE" + name: "&dФиолетовый" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_PURPLE" + signature: "REPLACE_WITH_SIGNATURE_PURPLE" + } + sound: ${successSound} + message: "&dФиолетовый скин применён. Перезайди, чтобы увидеть изменения." + } + } + } + + { + slot: 14 + material: PLAYER_HEAD + texture: "REPLACE_WITH_PREVIEW_TEXTURE_HASH_BLUE" + name: "&9Синий" + click { + actions { + closeMenu: 1 + setSkin { + texture: "REPLACE_WITH_TEXTURE_BASE64_BLUE" + signature: "REPLACE_WITH_SIGNATURE_BLUE" + } + sound: ${successSound} + message: "&9Синий скин применён. Перезайди, чтобы увидеть изменения." + } + } + } + + { + slot: 16 + material: BARRIER + name: "&7Сбросить на стандартный" + lore: ["&7Вернуть исходный скин."] + click { + actions { + closeMenu: 1 + resetSkin: true + sound: ${clickSound} + message: "&7Стандартный скин восстановлен. Перезайди, чтобы увидеть изменения." + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/cosmetics/03-color-skins/meta.json b/examples/ru/cosmetics/03-color-skins/meta.json new file mode 100644 index 0000000..52bd74f --- /dev/null +++ b/examples/ru/cosmetics/03-color-skins/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Цветные скины", + "category": "cosmetics", + "level": "intermediate", + "features": ["setSkin", "resetSkin", "closeMenu-before-setSkin", "PLAYER_HEAD-texture"], + "dependencies": ["SkinsRestorer"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_skins", + "order": 3 +} diff --git a/examples/ru/donate/01-tiered-ranks/menu.conf b/examples/ru/donate/01-tiered-ranks/menu.conf new file mode 100644 index 0000000..c16b723 --- /dev/null +++ b/examples/ru/donate/01-tiered-ranks/menu.conf @@ -0,0 +1,166 @@ +# Донат-магазин с уровнями рангов - 4 ранга с двумя вариантами отображения HAS_RANK / NO_RANK +# Docs: https://abstractmenus.github.io/docs/ru/examples/donate/tiered-ranks +# Open: /ame_donate +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Донат-магазин - Ранги" +size: 5 + +activators { + command: ["ame_donate", "ame_support"] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "36-44" } + + # Заголовок + { + slot: 4 + material: NETHER_STAR + name: "&6&lПоддержать MyTestServer" + lore: ["&7Выберите ранг, чтобы узнать подробнее.", "&7Покупка рангов поддерживает сервер."] + } + + # ========== Knight ========== + # Вариант HAS_RANK: у игрока уже есть право knight, повторно купить нельзя. + { + slot: 19 + material: IRON_SWORD + name: "&7Knight &8(&aКуплено&8)" + lore: [ + "&7Ранг поддержки 1 уровня." + "" + "&aЭтот ранг уже куплен." + "&7Подумайте об апгрейде ниже." + ] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.knight" } + } + # Вариант NO_RANK: ранга ещё нет, показываем цену. + { + slot: 19 + material: IRON_SWORD + name: "&7Knight" + lore: [ + "&7Ранг поддержки 1 уровня." + "" + "&7Включает:" + "&7- &fцветной ник в чате" + "&7- &f3 точки дома" + "" + "&eЦена: &a$5" + "" + "&aЛевый клик - подробнее" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + # В рабочем варианте здесь открывалось бы меню подтверждения / страница покупки. + # В этом примере просто выводим сообщение. + message: "&aОткройте страницу доната https://example.org, чтобы купить Knight." + sound: ${clickSound} + } + } + } + + # ========== Paladin ========== + { + slot: 21 + material: GOLDEN_SWORD + name: "&6Paladin &8(&aКуплено&8)" + lore: ["&7Ранг поддержки 2 уровня.", "", "&aЭтот ранг уже куплен."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.paladin" } + } + { + slot: 21 + material: GOLDEN_SWORD + name: "&6Paladin" + lore: [ + "&7Ранг поддержки 2 уровня." + "" + "&7Включает:" + "&7- &fвсё, что у Knight" + "&7- &f5 точек дома" + "&7- &f/fly в строительных мирах" + "" + "&eЦена: &a$15" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aОткройте страницу доната https://example.org, чтобы купить Paladin." + sound: ${clickSound} + } + } + } + + # ========== Lord ========== + { + slot: 23 + material: DIAMOND_SWORD + name: "&bLord &8(&aКуплено&8)" + lore: ["&7Ранг поддержки 3 уровня.", "", "&aЭтот ранг уже куплен."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.lord" } + } + { + slot: 23 + material: DIAMOND_SWORD + name: "&bLord" + lore: [ + "&7Ранг поддержки 3 уровня." + "" + "&7Включает:" + "&7- &fвсё, что у Paladin" + "&7- &fкастомные сообщения о входе" + "&7- &f10 точек дома" + "" + "&eЦена: &a$30" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aОткройте страницу доната https://example.org, чтобы купить Lord." + sound: ${clickSound} + } + } + } + + # ========== Monarch ========== + { + slot: 25 + material: NETHERITE_SWORD + name: "&dMonarch &8(&aКуплено&8)" + lore: ["&7Ранг поддержки 4 уровня.", "", "&aЭтот ранг уже куплен."] + flags: HIDE_ATTRIBUTES + rules { permission: "myserver.rank.monarch" } + } + { + slot: 25 + material: NETHERITE_SWORD + name: "&dMonarch" + lore: [ + "&7Ранг поддержки 4 уровня, самый высокий." + "" + "&7Включает:" + "&7- &fвсё, что у Lord" + "&7- &fэффект частиц при входе" + "&7- &fприоритет в очереди при заполнении" + "" + "&eЦена: &a$60" + ] + flags: HIDE_ATTRIBUTES + click { + actions { + message: "&aОткройте страницу доната https://example.org, чтобы купить Monarch." + sound: ${clickSound} + } + } + } + + ${buttonClose} { slot: 40 } +] diff --git a/examples/ru/donate/01-tiered-ranks/meta.json b/examples/ru/donate/01-tiered-ranks/meta.json new file mode 100644 index 0000000..300f5fe --- /dev/null +++ b/examples/ru/donate/01-tiered-ranks/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Донат-ранги по уровням", + "category": "donate", + "level": "intermediate", + "features": ["dual-item-display", "permission-rule", "command-aliases", "tier-pricing"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_donate", + "order": 1 +} diff --git a/examples/ru/donate/02-item-store/menu.conf b/examples/ru/donate/02-item-store/menu.conf new file mode 100644 index 0000000..6e7d22a --- /dev/null +++ b/examples/ru/donate/02-item-store/menu.conf @@ -0,0 +1,111 @@ +# Донат-магазин предметов - покупка внутриигровых предметов за донатные токены +# Docs: https://abstractmenus.github.io/docs/ru/examples/donate/item-store +# Open: /ame_store +# Requires: Vault (или PlayerPoints addon для донатных токенов) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Донат-магазин предметов" +size: 3 + +activators { + command: "ame_store" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Заголовок + { + slot: 4 + material: NETHER_STAR + name: "&6&lДонат-предметы" + lore: ["&7Особые предметы, которые можно купить", "&7за донатную валюту."] + } + + # Мифический меч + { + slot: 11 + material: NETHERITE_SWORD + name: "&dМифический меч" + lore: [ + "&7Уникальный зачарованный меч." + "" + "&7Острота V" + "&7Прочность III" + "&7Починка" + "" + "&eЦена: &a1000 токенов" + ] + flags: HIDE_ATTRIBUTES + click { + rules { money: 1000 } + actions { + takeMoney: 1000 + itemAdd { + material: NETHERITE_SWORD + name: "&dМифический меч" + enchantments { + sharpness: 5 + unbreaking: 3 + mending: 1 + } + } + sound: ${successSound} + message: "&dВы получили Мифический меч!" + } + denyActions: ${denyNoMoney} + } + } + + # Постоянный токен полёта + { + slot: 13 + material: ELYTRA + name: "&bЭлитры" + lore: [ + "&7Ванильные элитры для полёта в выживании." + "" + "&eЦена: &a500 токенов" + ] + flags: HIDE_ATTRIBUTES + click { + rules { money: 500 } + actions { + takeMoney: 500 + itemAdd { material: ELYTRA } + sound: ${successSound} + message: "&aЭлитры добавлены в инвентарь." + } + denyActions: ${denyNoMoney} + } + } + + # Набор голов + { + slot: 15 + material: PLAYER_HEAD + name: "&6Набор декоративных голов (5x)" + lore: [ + "&7Пять голов игрока с кастомными" + "&7текстурами для декора." + "" + "&eЦена: &a100 токенов" + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd: [ + { material: PLAYER_HEAD, count: 5 } + ] + sound: ${successSound} + message: "&aНабор голов выдан." + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/donate/02-item-store/meta.json b/examples/ru/donate/02-item-store/meta.json new file mode 100644 index 0000000..f2069d9 --- /dev/null +++ b/examples/ru/donate/02-item-store/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Магазин предметов", + "category": "donate", + "level": "intermediate", + "features": ["money-rule", "takeMoney", "itemAdd-with-enchantments", "donor-currency"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_store", + "order": 2 +} diff --git a/examples/ru/hub-and-nav/01-main-hub/menu.conf b/examples/ru/hub-and-nav/01-main-hub/menu.conf new file mode 100644 index 0000000..74d6dd9 --- /dev/null +++ b/examples/ru/hub-and-nav/01-main-hub/menu.conf @@ -0,0 +1,82 @@ +# Главный хаб - точка входа для примеров пака +# Docs: https://abstractmenus.github.io/docs/ru/examples/hub-and-nav/main-hub +# Открыть: /ame_hub +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Пак примеров - Главный хаб" +size: 3 + +activators { + command: "ame_hub" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Заголовок + { + slot: 4 + material: NETHER_STAR + name: "&6&lПак примеров AbstractMenus" + lore: ["&7Кликни по плитке, чтобы открыть пример."] + } + + # Магазины + { + slot: 10 + material: CAKE + name: "&aМагазин тортов" + lore: ["&7Простой магазин с money + itemAdd.", "", "&8Открывает /ame_cake_shop"] + click { + openMenu: "ame_cake_shop" + } + } + + { + slot: 11 + material: BREAD + name: "&aМагазин с категориями" + lore: ["&7Хаб + навигация по подменю.", "", "&8Открывает /ame_shop"] + click { + openMenu: "ame_shop" + } + } + + # Состояние + { + slot: 12 + material: BOOK + name: "&aСчётчик и переключатель" + lore: ["&7Переменные на игрока в действии.", "", "&8Открывает /ame_counter"] + click { + openMenu: "ame_counter" + } + } + + # Инфо + { + slot: 13 + material: WRITTEN_BOOK + name: "&aПравила сервера" + lore: ["&7Меню только для чтения.", "", "&8Открывает /ame_rules"] + click { + openMenu: "ame_rules" + } + } + + # Киты + { + slot: 14 + material: CHEST + name: "&aБазовый кит" + lore: ["&7Набор наград в один клик.", "", "&8Открывает /ame_kit"] + click { + openMenu: "ame_kit" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/hub-and-nav/01-main-hub/meta.json b/examples/ru/hub-and-nav/01-main-hub/meta.json new file mode 100644 index 0000000..522bbe6 --- /dev/null +++ b/examples/ru/hub-and-nav/01-main-hub/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Главный хаб", + "category": "hub-and-nav", + "level": "beginner", + "features": ["openMenu", "menu-launcher"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_hub", + "order": 1 +} diff --git a/examples/ru/hub-and-nav/02-sub-hub/menu.conf b/examples/ru/hub-and-nav/02-sub-hub/menu.conf new file mode 100644 index 0000000..dbde53b --- /dev/null +++ b/examples/ru/hub-and-nav/02-sub-hub/menu.conf @@ -0,0 +1,64 @@ +# Под-хаб - вторичный хаб с кнопкой назад в главный хаб +# Docs: https://abstractmenus.github.io/docs/ru/examples/hub-and-nav/sub-hub +# Открыть: /ame_subhub ИЛИ кликнуть по плитке "Прочее" в главном хабе +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Прочие возможности" +size: 3 + +activators { + command: "ame_subhub" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Заголовок + { + slot: 4 + material: NETHER_STAR + name: "&6&lПрочие возможности" + lore: ["&7То, что не поместилось в главный хаб."] + } + + # Выбор тегов + { + slot: 11 + material: PAPER + name: "&aТеги" + lore: ["&7Выбери тег для чата.", "", "&8Открывает /ame_tags"] + click { openMenu: "ame_tags" } + } + + # Рулетка + { + slot: 13 + material: NETHER_STAR + name: "&aРулетка" + lore: ["&7Испытай удачу.", "", "&8Открывает /ame_roulette"] + click { openMenu: "ame_roulette" } + } + + # Голосование + { + slot: 15 + material: BEACON + name: "&aЕжедневное голосование" + lore: ["&7Голосуй и получай награды.", "", "&8Открывает /ame_vote"] + click { openMenu: "ame_vote" } + } + + # Назад в главный хаб + { + slot: 18 + material: ARROW + name: "&eНазад в главный хаб" + lore: ["&7Вернуться к /ame_hub."] + click { openMenu: "ame_hub" } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/hub-and-nav/02-sub-hub/meta.json b/examples/ru/hub-and-nav/02-sub-hub/meta.json new file mode 100644 index 0000000..b004722 --- /dev/null +++ b/examples/ru/hub-and-nav/02-sub-hub/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Под-хаб", + "category": "hub-and-nav", + "level": "beginner", + "features": ["openMenu", "back-button-pattern", "secondary-hub"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_subhub", + "order": 2 +} diff --git a/examples/ru/hub-and-nav/03-breadcrumb-chain/menu.conf b/examples/ru/hub-and-nav/03-breadcrumb-chain/menu.conf new file mode 100644 index 0000000..f924da3 --- /dev/null +++ b/examples/ru/hub-and-nav/03-breadcrumb-chain/menu.conf @@ -0,0 +1,71 @@ +# Цепочка хлебных крошек - 3 вложенных меню с openMenuCtx для динамической навигации назад +# Docs: https://abstractmenus.github.io/docs/ru/examples/hub-and-nav/breadcrumb-chain +# Открыть: /ame_chain +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Уровень 1 + ame_chain_l1 { + title: "&6Уровень 1" + size: 1 + activators { command: "ame_chain" } + items: [ + { + slot: 4 + material: BLUE_WOOL + name: "&aГлубже" + lore: ["&7Открыть Уровень 2."] + click { openMenu: "ame_chain_l2" } + } + ${buttonClose} { slot: 8 } + ] + } + + # Уровень 2 + ame_chain_l2 { + title: "&6Уровень 2" + size: 1 + items: [ + { + slot: 4 + material: GREEN_WOOL + name: "&aГлубже" + lore: ["&7Открыть Уровень 3."] + click { openMenu: "ame_chain_l3" } + } + { + slot: 0 + material: ARROW + name: "&eНазад на Уровень 1" + click { openMenu: "ame_chain_l1" } + } + ] + } + + # Уровень 3 + ame_chain_l3 { + title: "&6Уровень 3 (самый глубокий)" + size: 1 + items: [ + { + slot: 4 + material: GOLD_BLOCK + name: "&6Ты дошёл до дна" + lore: [ + "&7Уровня 4 нет." + "&7Используй стрелку назад, чтобы выбраться." + ] + } + { + slot: 0 + material: ARROW + name: "&eНазад на Уровень 2" + click { openMenu: "ame_chain_l2" } + } + ] + } + +} diff --git a/examples/ru/hub-and-nav/03-breadcrumb-chain/meta.json b/examples/ru/hub-and-nav/03-breadcrumb-chain/meta.json new file mode 100644 index 0000000..082efd1 --- /dev/null +++ b/examples/ru/hub-and-nav/03-breadcrumb-chain/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Цепочка хлебных крошек", + "category": "hub-and-nav", + "level": "intermediate", + "features": ["multi-menu-file", "openMenu-chain", "manual-back-navigation"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_chain", + "order": 3 +} diff --git a/examples/ru/info-pages/01-rules/menu.conf b/examples/ru/info-pages/01-rules/menu.conf new file mode 100644 index 0000000..af1b9c9 --- /dev/null +++ b/examples/ru/info-pages/01-rules/menu.conf @@ -0,0 +1,91 @@ +# Правила сервера - информационное меню только для чтения +# Документация: https://abstractmenus.github.io/docs/ru/examples/info-pages/rules +# Открыть: /ame_rules +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Правила сервера" +size: 5 + +activators { + command: "ame_rules" +} + +items: [ + # Декоративные рамки + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "36-44" } + + # Заголовок + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lПравила MyTestServer" + lore: [ + "&7Читайте внимательно." + "&7Нарушение любого правила может привести к бану." + ] + } + + # Правило 1 + { + slot: 19 + material: PAPER + name: "&e1. Без гриферства" + lore: [ + "&7Не разрушайте и не изменяйте" + "&7постройки других игроков без" + "&7их явного разрешения." + "" + "&8Первое нарушение: предупреждение" + "&8Повторное: бан на 7 дней" + ] + } + + # Правило 2 + { + slot: 21 + material: PAPER + name: "&e2. Вежливость в чате" + lore: [ + "&7Никаких оскорблений, травли" + "&7и спама. Английский в основном чате," + "&7другие языки - в своих каналах." + "" + "&8Первое нарушение: мут на 1 час" + "&8Повторное: бессрочный мут" + ] + } + + # Правило 3 + { + slot: 23 + material: PAPER + name: "&e3. Без читов" + lore: [ + "&7Читы, x-ray, автокликеры" + "&7и подобные моды запрещены." + "&7Optifine и шейдеры разрешены." + "" + "&8Первое нарушение: бессрочный бан" + ] + } + + # Правило 4 + { + slot: 25 + material: PAPER + name: "&e4. Без продажи за реальные деньги" + lore: [ + "&7Продажа предметов, привилегий" + "&7или услуг за реальные деньги запрещена." + "&7Используйте только официальное меню доната." + "" + "&8Первое нарушение: бессрочный бан" + ] + } + + # Кнопка закрытия + ${buttonClose} { slot: 40 } +] diff --git a/examples/ru/info-pages/01-rules/meta.json b/examples/ru/info-pages/01-rules/meta.json new file mode 100644 index 0000000..14223a4 --- /dev/null +++ b/examples/ru/info-pages/01-rules/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правила сервера", + "category": "info-pages", + "level": "beginner", + "features": ["lore", "static-display", "no-click-handlers"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_rules", + "order": 1 +} diff --git a/examples/ru/info-pages/02-server-links/menu.conf b/examples/ru/info-pages/02-server-links/menu.conf new file mode 100644 index 0000000..5963685 --- /dev/null +++ b/examples/ru/info-pages/02-server-links/menu.conf @@ -0,0 +1,91 @@ +# Ссылки сервера - внешние ссылки через openBook +# Документация: https://abstractmenus.github.io/docs/ru/examples/info-pages/server-links +# Открыть: /ame_links +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Полезные ссылки" +size: 3 + +activators { + command: "ame_links" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Заголовок + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lРесурсы MyTestServer" + lore: ["&7Кликните по плитке, чтобы получить ссылку.", "&7Книги открывают кликабельные URL."] + } + + # Discord - открывает книгу со ссылкой-приглашением. + { + slot: 11 + material: PURPLE_DYE + name: "&5Discord" + lore: ["&7Присоединяйтесь к нашему Discord-серверу.", "", "&aКлик - открыть ссылку"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&5Discord" + pages: [ + "Кликните по ссылке ниже, чтобы открыть приглашение в Discord:\n\nhttps://discord.gg/example" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + # Сайт + { + slot: 13 + material: COMPASS + name: "&aСайт" + lore: ["&7Посетите наш сайт.", "", "&aКлик - открыть ссылку"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&aСайт" + pages: [ + "Кликните по ссылке ниже, чтобы перейти на наш сайт:\n\nhttps://example.org" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + # GitHub / исходники + { + slot: 15 + material: WRITABLE_BOOK + name: "&8GitHub" + lore: ["&7Исходный код и трекер задач.", "", "&aКлик - открыть ссылку"] + click { + actions { + openBook { + author: "MyTestServer" + title: "&8GitHub" + pages: [ + "Кликните по ссылке ниже, чтобы открыть наш GitHub:\n\nhttps://github.com/example" + ] + } + sound: ${clickSound} + closeMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/info-pages/02-server-links/meta.json b/examples/ru/info-pages/02-server-links/meta.json new file mode 100644 index 0000000..141b6e9 --- /dev/null +++ b/examples/ru/info-pages/02-server-links/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Ссылки сервера", + "category": "info-pages", + "level": "intermediate", + "features": ["openBook", "external-links", "closeMenu-with-action"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_links", + "order": 2 +} diff --git a/examples/ru/info-pages/03-faq/menu.conf b/examples/ru/info-pages/03-faq/menu.conf new file mode 100644 index 0000000..7b3e91d --- /dev/null +++ b/examples/ru/info-pages/03-faq/menu.conf @@ -0,0 +1,137 @@ +# FAQ - меню-хаб с вопросами, по клику открывается ответ в подменю +# Документация: https://abstractmenus.github.io/docs/ru/examples/info-pages/faq +# Открыть: /ame_faq +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Главная страница FAQ - список вопросов + ame_faq { + title: "&6Часто задаваемые вопросы" + size: 3 + + activators { + command: "ame_faq" + } + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: WRITTEN_BOOK + name: "&6&lFAQ" + lore: ["&7Кликните по любому вопросу, чтобы увидеть ответ."] + } + + { + slot: 10 + material: PAPER + name: "&aКак приватизировать территорию?" + click { openMenu: "ame_faq_a1" } + } + { + slot: 12 + material: PAPER + name: "&aЧто даёт VIP?" + click { openMenu: "ame_faq_a2" } + } + { + slot: 14 + material: PAPER + name: "&aКак пожаловаться на игрока?" + click { openMenu: "ame_faq_a3" } + } + { + slot: 16 + material: PAPER + name: "&aГде найти правила сервера?" + click { openMenu: "ame_rules" } + } + + ${buttonClose} { slot: 22 } + ] + } + + # Ответ 1 - Приватизация территории + ame_faq_a1 { + title: "&6FAQ - Приватизация" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: GRASS_BLOCK + name: "&aКак приватизировать территорию?" + lore: [ + "&7Используйте золотую лопату," + "&7чтобы отметить углы участка." + "" + "&7- ПКМ по первому углу" + "&7- ПКМ по противоположному углу" + "" + "&7Каждый блок стоит 1 единицу приватизации." + "&7Получайте больше за игру или покупайте" + "&7через /buyclaimblocks." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + + # Ответ 2 - Привилегии VIP + ame_faq_a2 { + title: "&6FAQ - Привилегии VIP" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: GOLDEN_HELMET + name: "&6Что даёт VIP?" + lore: [ + "&7VIP включает:" + "" + "&7- &fцветной ник в чате" + "&7- &f5 точек дома (вместо 1 базовой)" + "&7- &fежедневный VIP-кит" + "&7- &f50% скидку в магазине" + "&7- &fэффект частиц при заходе" + "" + "&7Получите VIP в &e/ame_donate&7." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + + # Ответ 3 - Жалобы на игроков + ame_faq_a3 { + title: "&6FAQ - Жалобы" + size: 3 + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + { + slot: 13 + material: BARRIER + name: "&cЖалобы на игроков" + lore: [ + "&7Используйте &e/report <ник> <причина>&7" + "&7или напишите в нашем Discord:" + "&7&fhttps://discord.gg/example" + "" + "&7По возможности прикладывайте скриншоты." + "&7Модерация отвечает в течение нескольких часов." + ] + } + ${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } + ] + } + +} diff --git a/examples/ru/info-pages/03-faq/meta.json b/examples/ru/info-pages/03-faq/meta.json new file mode 100644 index 0000000..e82c7ab --- /dev/null +++ b/examples/ru/info-pages/03-faq/meta.json @@ -0,0 +1,12 @@ +{ + "title": "FAQ", + "category": "info-pages", + "level": "intermediate", + "features": ["multi-menu-file", "openMenu-navigation", "back-button-pattern", "menu-tree"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_faq", + "order": 3 +} diff --git a/examples/ru/kits-and-rewards/01-basic-kit/menu.conf b/examples/ru/kits-and-rewards/01-basic-kit/menu.conf new file mode 100644 index 0000000..a3b48cd --- /dev/null +++ b/examples/ru/kits-and-rewards/01-basic-kit/menu.conf @@ -0,0 +1,60 @@ +# Basic Kit - набор предметов в один клик +# Docs: https://abstractmenus.github.io/docs/ru/examples/kits-and-rewards/basic-kit +# Open: /ame_kit +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Стартовый кит" +size: 3 + +activators { + command: "ame_kit" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Описание кита + { + slot: 4 + material: PAPER + name: "&6&lСтартовый кит" + lore: [ + "&7Получи разовый набор стартовых предметов" + "&7и 50 опыта, чтобы начать своё приключение." + "" + "&eСодержимое:" + "&7- Железный меч" + "&7- Железная кирка" + "&7- Хлеб x16" + "&7- 50 опыта" + ] + } + + # Кнопка получения + { + slot: 13 + material: CHEST + name: "&aЗабрать кит" + lore: [ + "&7Нажми, чтобы получить кит." + ] + click { + actions { + itemAdd: [ + { material: IRON_SWORD } + { material: IRON_PICKAXE } + { material: BREAD, count: 16 } + ] + giveXp: 50 + sound: ${successSound} + message: "&aТы забрал стартовый кит!" + closeMenu: 20 + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/kits-and-rewards/01-basic-kit/meta.json b/examples/ru/kits-and-rewards/01-basic-kit/meta.json new file mode 100644 index 0000000..7943e57 --- /dev/null +++ b/examples/ru/kits-and-rewards/01-basic-kit/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Базовый кит", + "category": "kits-and-rewards", + "level": "beginner", + "features": ["itemAdd", "giveXp", "sound", "message", "closeMenu-delay"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_kit", + "order": 1 +} diff --git a/examples/ru/kits-and-rewards/02-daily-kit-cooldown/menu.conf b/examples/ru/kits-and-rewards/02-daily-kit-cooldown/menu.conf new file mode 100644 index 0000000..cb696a1 --- /dev/null +++ b/examples/ru/kits-and-rewards/02-daily-kit-cooldown/menu.conf @@ -0,0 +1,132 @@ +# Daily Kit with Cooldown - временные переменные + паттерн с двумя предметами +# Docs: https://abstractmenus.github.io/docs/ru/examples/kits-and-rewards/daily-kit-cooldown +# Open: /ame_daily_kit +# Requires: LuckPerms (для правила group на VIP-ките) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Ежедневные киты" +size: 3 +updateInterval: 20 + +activators { + command: "ame_daily_kit" +} + +# Шаблоны - общие для вариантов "доступен" и "на кулдауне" + +defaultKitTemplate { + slot: 11 + material: WOODEN_SWORD + name: "&aОбычный кит" + flags: HIDE_ATTRIBUTES + lore: [ + "&7В составе:" + "&7- Кожаный комплект брони" + "&7- Деревянный меч" + "&7- 32 жареной говядины" + "&7- 16 железных слитков" + ] +} + +vipKitTemplate { + slot: 15 + material: STONE_SWORD + name: "&6VIP-кит" + flags: HIDE_ATTRIBUTES + lore: [ + "&7В составе:" + "&7- Кожаный комплект брони" + "&7- Каменный меч" + "&7- 32 жареной говядины" + "&7- 32 железных слитка" + "&7- 4 золотых яблока" + "" + "&8Требуется: группа VIP" + ] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Обычный кит ========== + # Состояние ДОСТУПЕН: показывается, когда переменная кулдауна не существует. По клику - забрать кит. + ${defaultKitTemplate} { + lore: ${defaultKitTemplate.lore} [ + "" + "&aНажми, чтобы забрать" + ] + click { + actions { + itemAdd: [ + { material: LEATHER_HELMET } + { material: LEATHER_CHESTPLATE } + { material: LEATHER_LEGGINGS } + { material: LEATHER_BOOTS } + { material: WOODEN_SWORD } + { material: COOKED_BEEF, count: 32 } + { material: IRON_INGOT, count: 16 } + ] + # Ставим временную переменную, которая истекает через 1 день - суффикс ::1d. + setVarp: "ame_kit_default::1::1d" + sound: ${successSound} + message: "&aОбычный кит забран. Возвращайся через 24 часа." + refreshMenu: true + } + } + } + # Состояние КУЛДАУН: показывается только пока существует временная переменная. Отображает оставшееся время. + ${defaultKitTemplate} { + material: GRAY_DYE + lore: ${defaultKitTemplate.lore} [ + "" + "&cДоступен через &e%varpt_:ame_kit_default%" + ] + rules { + existVarp: "ame_kit_default" + } + } + + # ========== VIP-кит ========== + ${vipKitTemplate} { + lore: ${vipKitTemplate.lore} [ + "" + "&aНажми, чтобы забрать" + ] + click { + rules { + group: "vip" + } + actions { + itemAdd: [ + { material: LEATHER_HELMET } + { material: LEATHER_CHESTPLATE } + { material: LEATHER_LEGGINGS } + { material: LEATHER_BOOTS } + { material: STONE_SWORD } + { material: COOKED_BEEF, count: 32 } + { material: IRON_INGOT, count: 32 } + { material: GOLDEN_APPLE, count: 4 } + ] + setVarp: "ame_kit_vip::1::1d" + sound: ${successSound} + message: "&6VIP-кит забран. Возвращайся через 24 часа." + refreshMenu: true + } + denyActions: ${denyNoPerm} + } + } + ${vipKitTemplate} { + material: GRAY_DYE + lore: ${vipKitTemplate.lore} [ + "" + "&cДоступен через &e%varpt_:ame_kit_vip%" + ] + rules { + existVarp: "ame_kit_vip" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/kits-and-rewards/02-daily-kit-cooldown/meta.json b/examples/ru/kits-and-rewards/02-daily-kit-cooldown/meta.json new file mode 100644 index 0000000..4a5e11a --- /dev/null +++ b/examples/ru/kits-and-rewards/02-daily-kit-cooldown/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Ежедневный кит с кулдауном", + "category": "kits-and-rewards", + "level": "advanced", + "features": ["setVarp-temporal", "existVarp", "varpt-placeholder", "group-rule", "template-substitution", "lore-extension", "dual-item-display"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_daily_kit", + "order": 2 +} diff --git a/examples/ru/kits-and-rewards/03-tiered-kits/menu.conf b/examples/ru/kits-and-rewards/03-tiered-kits/menu.conf new file mode 100644 index 0000000..685e2fd --- /dev/null +++ b/examples/ru/kits-and-rewards/03-tiered-kits/menu.conf @@ -0,0 +1,114 @@ +# Tiered Kits - 3 тира китов с правами доступа, без кулдауна +# Docs: https://abstractmenus.github.io/docs/ru/examples/kits-and-rewards/tiered-kits +# Open: /ame_kit_tiers +# Requires: LuckPerms + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Киты по уровням" +size: 3 + +activators { + command: "ame_kit_tiers" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Обычный кит (всем) ========== + { + slot: 11 + material: WOODEN_SWORD + name: "&7Обычный кит" + flags: HIDE_ATTRIBUTES + lore: [ + "&7В составе:" + "&7- Деревянный меч" + "&7- 8 хлеба" + "" + "&aБесплатно для всех" + "&aНажми, чтобы забрать" + ] + click { + actions { + itemAdd: [ + { material: WOODEN_SWORD } + { material: BREAD, count: 8 } + ] + sound: ${successSound} + message: "&aОбычный кит забран." + } + } + } + + # ========== VIP-кит (по правам) ========== + { + slot: 13 + material: STONE_SWORD + name: "&6VIP-кит" + flags: HIDE_ATTRIBUTES + lore: [ + "&7В составе:" + "&7- Каменный меч" + "&7- Железная броня (полный комплект)" + "&7- 16 хлеба, 4 золотых яблока" + "" + "&8Требуется: ранг VIP" + "&aНажми, чтобы забрать" + ] + click { + rules { permission: "abstractmenus.vip" } + actions { + itemAdd: [ + { material: STONE_SWORD } + { material: IRON_HELMET } + { material: IRON_CHESTPLATE } + { material: IRON_LEGGINGS } + { material: IRON_BOOTS } + { material: BREAD, count: 16 } + { material: GOLDEN_APPLE, count: 4 } + ] + sound: ${successSound} + message: "&6VIP-кит забран." + } + denyActions: ${denyNoPerm} + } + } + + # ========== Elite-кит (выше уровень прав) ========== + { + slot: 15 + material: DIAMOND_SWORD + name: "&dElite-кит" + flags: HIDE_ATTRIBUTES + lore: [ + "&7В составе:" + "&7- Алмазный меч" + "&7- Алмазная броня (полный комплект)" + "&7- 32 хлеба, 8 зачарованных золотых яблок" + "" + "&8Требуется: ранг Elite" + "&aНажми, чтобы забрать" + ] + click { + rules { permission: "abstractmenus.elite" } + actions { + itemAdd: [ + { material: DIAMOND_SWORD } + { material: DIAMOND_HELMET } + { material: DIAMOND_CHESTPLATE } + { material: DIAMOND_LEGGINGS } + { material: DIAMOND_BOOTS } + { material: BREAD, count: 32 } + { material: ENCHANTED_GOLDEN_APPLE, count: 8 } + ] + sound: ${successSound} + message: "&dElite-кит забран." + } + denyActions: ${denyNoPerm} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/kits-and-rewards/03-tiered-kits/meta.json b/examples/ru/kits-and-rewards/03-tiered-kits/meta.json new file mode 100644 index 0000000..1b2c989 --- /dev/null +++ b/examples/ru/kits-and-rewards/03-tiered-kits/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Киты по уровням", + "category": "kits-and-rewards", + "level": "intermediate", + "features": ["itemAdd-list", "permission-rule", "denyActions-substitution", "no-cooldown"], + "dependencies": ["LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_kit_tiers", + "order": 3 +} diff --git a/examples/ru/shops/01-cake-shop/menu.conf b/examples/ru/shops/01-cake-shop/menu.conf new file mode 100644 index 0000000..d534bb3 --- /dev/null +++ b/examples/ru/shops/01-cake-shop/menu.conf @@ -0,0 +1,101 @@ +# Cake Shop - простой пример магазина +# Docs: https://abstractmenus.github.io/docs/ru/examples/shops/cake-shop +# Open: /ame_cake_shop +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Магазин тортов" +size: 3 + +activators { + command: "ame_cake_shop" +} + +items: [ + # Верхняя и нижняя рамки, через общий шаблон + диапазон слотов + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Декоративный заголовок по центру верхнего ряда + ${commonHeader} { slot: 4 } + + # Купить один торт за $100 + { + slot: 11 + material: CAKE + name: "&6Сладкий торт" + lore: [ + "&7Вкусный торт." + "" + "&eЦена: &a$100" + "" + "&8Кликните, чтобы купить" + ] + click { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd { material: CAKE } + sound: ${successSound} + message: "&aВы купили торт!" + } + denyActions: ${denyNoMoney} + } + } + + # Купить 4 печеньки за $25 + { + slot: 13 + material: COOKIE + count: 4 + name: "&6Печенье x4" + lore: [ + "&7Пачка из четырёх печенек." + "" + "&eЦена: &a$25" + "" + "&8Кликните, чтобы купить" + ] + click { + rules { money: 25 } + actions { + takeMoney: 25 + itemAdd { + material: COOKIE + count: 4 + } + sound: ${successSound} + message: "&aВы купили 4 печенья!" + } + denyActions: ${denyNoMoney} + } + } + + # Купить тыквенный пирог за $60 + { + slot: 15 + material: PUMPKIN_PIE + name: "&6Тыквенный пирог" + lore: [ + "&7Сезонный фаворит." + "" + "&eЦена: &a$60" + "" + "&8Кликните, чтобы купить" + ] + click { + rules { money: 60 } + actions { + takeMoney: 60 + itemAdd { material: PUMPKIN_PIE } + sound: ${successSound} + message: "&aВы купили тыквенный пирог!" + } + denyActions: ${denyNoMoney} + } + } + + # Кнопка закрытия + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/shops/01-cake-shop/meta.json b/examples/ru/shops/01-cake-shop/meta.json new file mode 100644 index 0000000..5a7ae93 --- /dev/null +++ b/examples/ru/shops/01-cake-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Магазин тортов", + "category": "shops", + "level": "beginner", + "features": ["money", "takeMoney", "itemAdd"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_cake_shop", + "order": 1 +} diff --git a/examples/ru/shops/02-paginated-shop/menu.conf b/examples/ru/shops/02-paginated-shop/menu.conf new file mode 100644 index 0000000..a397b85 --- /dev/null +++ b/examples/ru/shops/02-paginated-shop/menu.conf @@ -0,0 +1,201 @@ +# Multi-Category Shop - паттерн навигации хаб + подмагазины +# Docs: https://abstractmenus.github.io/docs/ru/examples/shops/paginated-shop +# Open: /ame_shop +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +menus { + + # Главный хаб: открывается по /ame_shop. Плитки ведут в подмагазины. + ame_shop { + title: "&6Магазин предметов" + size: 1 + + activators { + command: "ame_shop" + } + + items: [ + { + slot: 2 + material: BREAD + name: "&aЕда" + lore: ["&7Хлеб, яблоки, золотая морковь."] + click { + openMenu: "ame_shop_food" + } + } + { + slot: 6 + material: IRON_PICKAXE + name: "&aИнструменты" + lore: ["&7Кирки, топоры, лопаты."] + flags: HIDE_ATTRIBUTES + click { + openMenu: "ame_shop_tools" + } + } + ${buttonClose} { slot: 8 } + ] + } + + # Подмагазин еды: 4 предмета, демонстрация паттерна покупки. + # Паттерн повторяется для любой другой категории - просто копируйте это меню и меняйте предметы. + ame_shop_food { + title: "&6Магазин > Еда" + size: 3 + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-25" } + + { + slot: 10 + material: APPLE + count: 16 + name: "&aЯблоко x16" + lore: ["", "&eЦена: &a$50", "", "&8Кликните, чтобы купить"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: APPLE, count: 16 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 12 + material: BREAD + count: 8 + name: "&aХлеб x8" + lore: ["", "&eЦена: &a$60", "", "&8Кликните, чтобы купить"] + click { + rules { money: 60 } + actions { + takeMoney: 60 + itemAdd { material: BREAD, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 14 + material: COOKED_BEEF + count: 8 + name: "&aСтейк x8" + lore: ["", "&eЦена: &a$120", "", "&8Кликните, чтобы купить"] + click { + rules { money: 120 } + actions { + takeMoney: 120 + itemAdd { material: COOKED_BEEF, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 16 + material: GOLDEN_CARROT + count: 4 + name: "&6Золотая морковь x4" + lore: ["", "&eЦена: &a$300", "", "&8Кликните, чтобы купить"] + click { + rules { money: 300 } + actions { + takeMoney: 300 + itemAdd { material: GOLDEN_CARROT, count: 4 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + # Назад в главный хаб + { + slot: 26 + material: ARROW + name: "&eНазад" + lore: ["&7Вернуться в главное меню магазина"] + click { + openMenu: "ame_shop" + } + } + ] + } + + # Подмагазин инструментов: 3 предмета, демонстрация флагов и более высоких цен. + ame_shop_tools { + title: "&6Магазин > Инструменты" + size: 3 + + items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-25" } + + { + slot: 11 + material: STONE_PICKAXE + name: "&aКаменная кирка" + lore: ["", "&eЦена: &a$200", "", "&8Кликните, чтобы купить"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 200 } + actions { + takeMoney: 200 + itemAdd { material: STONE_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 13 + material: IRON_PICKAXE + name: "&aЖелезная кирка" + lore: ["", "&eЦена: &a$800", "", "&8Кликните, чтобы купить"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 800 } + actions { + takeMoney: 800 + itemAdd { material: IRON_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 15 + material: DIAMOND_PICKAXE + name: "&bАлмазная кирка" + lore: ["", "&eЦена: &a$3000", "", "&8Кликните, чтобы купить"] + flags: HIDE_ATTRIBUTES + click { + rules { money: 3000 } + actions { + takeMoney: 3000 + itemAdd { material: DIAMOND_PICKAXE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + { + slot: 26 + material: ARROW + name: "&eНазад" + lore: ["&7Вернуться в главное меню магазина"] + click { + openMenu: "ame_shop" + } + } + ] + } + +} diff --git a/examples/ru/shops/02-paginated-shop/meta.json b/examples/ru/shops/02-paginated-shop/meta.json new file mode 100644 index 0000000..7ebb53d --- /dev/null +++ b/examples/ru/shops/02-paginated-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Магазин по категориям", + "category": "shops", + "level": "intermediate", + "features": ["money", "takeMoney", "itemAdd", "openMenu", "menus-block"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_shop", + "order": 2 +} diff --git a/examples/ru/shops/03-rank-gated-shop/menu.conf b/examples/ru/shops/03-rank-gated-shop/menu.conf new file mode 100644 index 0000000..2dfef06 --- /dev/null +++ b/examples/ru/shops/03-rank-gated-shop/menu.conf @@ -0,0 +1,80 @@ +# Rank-Gated Shop - паттерн отображения двух вариантов HAS/NO_PERM +# Docs: https://abstractmenus.github.io/docs/ru/examples/shops/rank-gated-shop +# Open: /ame_rank_shop +# Requires: Vault, LuckPerms (или любой провайдер прав) + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Магазин с проверкой ранга" +size: 3 + +activators { + command: "ame_rank_shop" +} + +# Переиспользуемый обработчик клика для VIP-скидки - списывает сниженную цену. +discountClick { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + message: "&aКуплено со скидкой VIP." + } + denyActions: ${denyNoMoney} +} + +# Переиспользуемый обработчик клика для обычной цены. +regularClick { + rules { money: 100 } + actions { + takeMoney: 100 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + message: "&aКуплено по обычной цене." + } + denyActions: ${denyNoMoney} +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Золотое яблоко - два варианта по правилу VIP ========== + + # VIP-вариант: со скидкой, виден только VIP-игрокам. + { + slot: 13 + material: GOLDEN_APPLE + name: "&6Золотое яблоко &8(&aцена VIP&8)" + lore: [ + "&7Премиальный фрукт." + "" + "&eЦена: &a$50 &m$100" + "&8(скидка VIP 50%)" + "" + "&aКликните, чтобы купить" + ] + rules { permission: "abstractmenus.vip" } + click: ${discountClick} + } + + # Обычный вариант: полная цена, показывается всем остальным (без правил = всегда видим, + # но VIP-вариант перекрывает его, когда правило VIP проходит). + { + slot: 13 + material: GOLDEN_APPLE + name: "&6Золотое яблоко" + lore: [ + "&7Премиальный фрукт." + "" + "&eЦена: &a$100" + "" + "&aКликните, чтобы купить" + "&8Ранг VIP даёт скидку 50%." + ] + click: ${regularClick} + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/shops/03-rank-gated-shop/meta.json b/examples/ru/shops/03-rank-gated-shop/meta.json new file mode 100644 index 0000000..f59e203 --- /dev/null +++ b/examples/ru/shops/03-rank-gated-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Магазин с проверкой ранга", + "category": "shops", + "level": "intermediate", + "features": ["dual-item-display", "permission-rule", "money-rule", "discount-tier", "click-handler-template"], + "dependencies": ["Vault", "LuckPerms"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_rank_shop", + "order": 3 +} diff --git a/examples/ru/shops/04-sell-and-buy-shop/menu.conf b/examples/ru/shops/04-sell-and-buy-shop/menu.conf new file mode 100644 index 0000000..6604a3c --- /dev/null +++ b/examples/ru/shops/04-sell-and-buy-shop/menu.conf @@ -0,0 +1,139 @@ +# Sell-and-Buy Shop - левый клик покупает, правый продаёт +# Docs: https://abstractmenus.github.io/docs/ru/examples/shops/sell-and-buy-shop +# Open: /ame_buysell +# Requires: Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Торговец" +size: 3 + +activators { + command: "ame_buysell" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Пшеница - левый клик покупает 16 шт за $20, правый продаёт 16 шт за $15. + { + slot: 11 + material: WHEAT + count: 16 + name: "&6Пшеница &8(стак из 16)" + lore: [ + "&7Покупка: &a$20" + "&7Продажа: &a$15" + "" + "&aЛевый клик - купить" + "&cПравый клик - продать" + ] + click { + left { + rules { money: 20 } + actions { + takeMoney: 20 + itemAdd { material: WHEAT, count: 16 } + sound: ${successSound} + message: "&aКуплено 16 пшеницы за $20." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: WHEAT, count: 16 }] } + actions { + itemRemove: [{ material: WHEAT, count: 16 }] + giveMoney: 15 + sound: ${successSound} + message: "&aПродано 16 пшеницы за $15." + } + denyActions { + sound: ${deniedSound} + message: "&cУ вас нет 16 пшеницы для продажи." + } + } + } + } + + # Железо - левый/правый с более высокой ценовой категорией + { + slot: 13 + material: IRON_INGOT + count: 8 + name: "&fЖелезный слиток &8(стак из 8)" + lore: [ + "&7Покупка: &a$80" + "&7Продажа: &a$60" + "" + "&aЛевый клик - купить" + "&cПравый клик - продать" + ] + click { + left { + rules { money: 80 } + actions { + takeMoney: 80 + itemAdd { material: IRON_INGOT, count: 8 } + sound: ${successSound} + message: "&aКуплено 8 железных слитков за $80." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: IRON_INGOT, count: 8 }] } + actions { + itemRemove: [{ material: IRON_INGOT, count: 8 }] + giveMoney: 60 + sound: ${successSound} + message: "&aПродано 8 железных слитков за $60." + } + denyActions { + sound: ${deniedSound} + message: "&cУ вас нет 8 железных слитков для продажи." + } + } + } + } + + # Алмаз - больший разрыв цены для ценностей + { + slot: 15 + material: DIAMOND + name: "&bАлмаз" + lore: [ + "&7Покупка: &a$500" + "&7Продажа: &a$350" + "" + "&aЛевый клик - купить" + "&cПравый клик - продать" + ] + click { + left { + rules { money: 500 } + actions { + takeMoney: 500 + itemAdd { material: DIAMOND } + sound: ${successSound} + message: "&aКуплен 1 алмаз за $500." + } + denyActions: ${denyNoMoney} + } + right { + rules { inventoryItems: [{ material: DIAMOND, count: 1 }] } + actions { + itemRemove: [{ material: DIAMOND, count: 1 }] + giveMoney: 350 + sound: ${successSound} + message: "&aПродан 1 алмаз за $350." + } + denyActions { + sound: ${deniedSound} + message: "&cУ вас нет алмаза для продажи." + } + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/shops/04-sell-and-buy-shop/meta.json b/examples/ru/shops/04-sell-and-buy-shop/meta.json new file mode 100644 index 0000000..7c07c3c --- /dev/null +++ b/examples/ru/shops/04-sell-and-buy-shop/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Магазин покупки и продажи", + "category": "shops", + "level": "intermediate", + "features": ["click-types-left-right", "money-rule", "inventoryItems-rule", "takeMoney", "giveMoney", "itemRemove", "asymmetric-prices"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_buysell", + "order": 4 +} diff --git a/examples/ru/shops/05-npc-vendor/menu.conf b/examples/ru/shops/05-npc-vendor/menu.conf new file mode 100644 index 0000000..e514160 --- /dev/null +++ b/examples/ru/shops/05-npc-vendor/menu.conf @@ -0,0 +1,75 @@ +# NPC Vendor - магазин, открывающийся по клику на NPC из Citizens +# Docs: https://abstractmenus.github.io/docs/ru/examples/shops/npc-vendor +# Open: правый клик по NPC из Citizens с id 1 (замените на id вашего NPC) +# Requires: Citizens, Vault + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Торговец %activator_npc_name%" +size: 3 + +activators { + clickNPC: [1] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + { + slot: 4 + material: EMERALD + name: "&aДобро пожаловать, путник" + lore: ["&7Купите товары у %activator_npc_name%."] + } + + { + slot: 11 + material: BREAD + count: 8 + name: "&6Хлеб x8" + lore: ["&eЦена: &a$30"] + click { + rules { money: 30 } + actions { + takeMoney: 30 + itemAdd { material: BREAD, count: 8 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 13 + material: COOKED_BEEF + count: 4 + name: "&6Стейк x4" + lore: ["&eЦена: &a$50"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: COOKED_BEEF, count: 4 } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + { + slot: 15 + material: GOLDEN_APPLE + name: "&6Золотое яблоко" + lore: ["&eЦена: &a$200"] + click { + rules { money: 200 } + actions { + takeMoney: 200 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/shops/05-npc-vendor/meta.json b/examples/ru/shops/05-npc-vendor/meta.json new file mode 100644 index 0000000..d6670ca --- /dev/null +++ b/examples/ru/shops/05-npc-vendor/meta.json @@ -0,0 +1,12 @@ +{ + "title": "NPC-торговец", + "category": "shops", + "level": "intermediate", + "features": ["clickNPC-activator", "Citizens-integration", "%activator_npc_*%-placeholders", "money-rule"], + "dependencies": ["Citizens", "Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(ПКМ по NPC)", + "order": 5 +} diff --git a/examples/ru/snippets/01-rule-permission/menu.conf b/examples/ru/snippets/01-rule-permission/menu.conf new file mode 100644 index 0000000..ea83b10 --- /dev/null +++ b/examples/ru/snippets/01-rule-permission/menu.conf @@ -0,0 +1,28 @@ +# Сниппет: правило permission +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/rule-permission +# Открыть: /ame_snip_perm + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Правило permission" +size: 1 +activators { command: "ame_snip_perm" } + +items: [ + { + slot: 4 + material: DIAMOND + name: "&bНаграда для админа" + lore: ["&7Клик - только для админов."] + click { + rules { permission: "abstractmenus.admin" } + actions { + itemAdd { material: DIAMOND } + sound: ${successSound} + message: "&aАдминский алмаз получен." + } + denyActions: ${denyNoPerm} + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/01-rule-permission/meta.json b/examples/ru/snippets/01-rule-permission/meta.json new file mode 100644 index 0000000..1e2e71a --- /dev/null +++ b/examples/ru/snippets/01-rule-permission/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правило: permission", + "category": "snippets", + "level": "beginner", + "features": ["permission-rule", "denyActions-substitution"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_perm", + "order": 1 +} diff --git a/examples/ru/snippets/02-rule-money/menu.conf b/examples/ru/snippets/02-rule-money/menu.conf new file mode 100644 index 0000000..586a9ef --- /dev/null +++ b/examples/ru/snippets/02-rule-money/menu.conf @@ -0,0 +1,28 @@ +# Сниппет: правило money + takeMoney +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/rule-money +# Открыть: /ame_snip_money + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Правило money" +size: 1 +activators { command: "ame_snip_money" } + +items: [ + { + slot: 4 + material: GOLDEN_APPLE + name: "&6Купить золотое яблоко" + lore: ["&7Цена: &a$50"] + click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { material: GOLDEN_APPLE } + sound: ${successSound} + } + denyActions: ${denyNoMoney} + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/02-rule-money/meta.json b/examples/ru/snippets/02-rule-money/meta.json new file mode 100644 index 0000000..3fb04a1 --- /dev/null +++ b/examples/ru/snippets/02-rule-money/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правило: money", + "category": "snippets", + "level": "beginner", + "features": ["money-rule", "takeMoney"], + "dependencies": ["Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_money", + "order": 2 +} diff --git a/examples/ru/snippets/03-rule-chance/menu.conf b/examples/ru/snippets/03-rule-chance/menu.conf new file mode 100644 index 0000000..5b0608e --- /dev/null +++ b/examples/ru/snippets/03-rule-chance/menu.conf @@ -0,0 +1,22 @@ +# Сниппет: правило chance для вероятностного отображения +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/rule-chance +# Открыть: /ame_snip_chance + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Правило chance" +size: 1 +activators { command: "ame_snip_chance" } + +items: [ + # 30% шанс отрисовать этот редкий предмет в слоте 4 + { + slot: 4 + material: NETHER_STAR + name: "&dРедкая звезда" + lore: ["&7Появляется с шансом 30%."] + rules { chance: 30 } + } + # Запасной вариант: с шансом 70% слот остаётся пустым (без запасного предмета, просто пустой слот) + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/03-rule-chance/meta.json b/examples/ru/snippets/03-rule-chance/meta.json new file mode 100644 index 0000000..6c5e2f4 --- /dev/null +++ b/examples/ru/snippets/03-rule-chance/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правило: chance", + "category": "snippets", + "level": "beginner", + "features": ["chance-rule"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_chance", + "order": 3 +} diff --git a/examples/ru/snippets/04-rule-if-placeholder/menu.conf b/examples/ru/snippets/04-rule-if-placeholder/menu.conf new file mode 100644 index 0000000..c7d2f0d --- /dev/null +++ b/examples/ru/snippets/04-rule-if-placeholder/menu.conf @@ -0,0 +1,38 @@ +# Сниппет: правило if с выражением на плейсхолдерах +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/rule-if-placeholder +# Открыть: /ame_snip_if + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Правило if" +size: 1 +activators { command: "ame_snip_if" } + +items: [ + # Виден только игрокам с уровнем 10+. Сравнивает плейсхолдер через выражение. + { + slot: 4 + material: ENCHANTED_BOOK + name: "&aНаграда за уровень 10+" + lore: ["&7Виден всем, у кого 10-й уровень или выше."] + rules { + if: "%player_level% >= 10" + } + click { + actions { + itemAdd { material: EXPERIENCE_BOTTLE, count: 4 } + sound: ${successSound} + } + } + } + + # Запасное сообщение для игроков с низким уровнем + { + slot: 4 + material: BARRIER + name: "&cНедостаточно уровня" + lore: ["&7Получите 10-й уровень, чтобы забрать награду."] + } + + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/04-rule-if-placeholder/meta.json b/examples/ru/snippets/04-rule-if-placeholder/meta.json new file mode 100644 index 0000000..ae9d1c2 --- /dev/null +++ b/examples/ru/snippets/04-rule-if-placeholder/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правило: if (выражение с плейсхолдером)", + "category": "snippets", + "level": "intermediate", + "features": ["if-rule", "placeholder-comparison", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_if", + "order": 4 +} diff --git a/examples/ru/snippets/05-rule-and-or/menu.conf b/examples/ru/snippets/05-rule-and-or/menu.conf new file mode 100644 index 0000000..2b976b5 --- /dev/null +++ b/examples/ru/snippets/05-rule-and-or/menu.conf @@ -0,0 +1,42 @@ +# Сниппет: логические обёртки and / or +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/rule-and-or +# Открыть: /ame_snip_andor + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Логические правила" +size: 1 +activators { command: "ame_snip_andor" } + +items: [ + # AND - должны выполниться все вложенные правила. (Блок rules по умолчанию работает как AND, здесь явно для демонстрации.) + { + slot: 2 + material: DIAMOND_SWORD + name: "&bАдмин в CREATIVE" + lore: ["&7Нужно: право администратора И креативный режим."] + flags: HIDE_ATTRIBUTES + rules { + and { + permission: "abstractmenus.admin" + gamemode: CREATIVE + } + } + } + + # OR - выполняется как минимум одно вложенное правило. + { + slot: 6 + material: GOLDEN_APPLE + name: "&aVIP или богач" + lore: ["&7Нужно: право VIP ИЛИ $1000."] + rules { + or { + permission: "abstractmenus.vip" + money: 1000 + } + } + } + + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/05-rule-and-or/meta.json b/examples/ru/snippets/05-rule-and-or/meta.json new file mode 100644 index 0000000..b85ae98 --- /dev/null +++ b/examples/ru/snippets/05-rule-and-or/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Правила: and / or", + "category": "snippets", + "level": "intermediate", + "features": ["and-wrapper", "or-wrapper", "logical-composition"], + "dependencies": ["LuckPerms", "Vault"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_andor", + "order": 5 +} diff --git a/examples/ru/snippets/06-action-give-item/menu.conf b/examples/ru/snippets/06-action-give-item/menu.conf new file mode 100644 index 0000000..e4730fe --- /dev/null +++ b/examples/ru/snippets/06-action-give-item/menu.conf @@ -0,0 +1,28 @@ +# Сниппет: действие itemAdd +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/action-give-item +# Открыть: /ame_snip_give + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Действие itemAdd" +size: 1 +activators { command: "ame_snip_give" } + +items: [ + { + slot: 4 + material: WOODEN_PICKAXE + name: "&aДайте мне инструмент" + lore: ["&7Кликните, чтобы получить деревянную кирку."] + click { + actions { + itemAdd { + material: WOODEN_PICKAXE + name: "&aСтартовая кирка" + } + sound: ${successSound} + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/06-action-give-item/meta.json b/examples/ru/snippets/06-action-give-item/meta.json new file mode 100644 index 0000000..30b9a74 --- /dev/null +++ b/examples/ru/snippets/06-action-give-item/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Действие: itemAdd", + "category": "snippets", + "level": "beginner", + "features": ["itemAdd"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_give", + "order": 6 +} diff --git a/examples/ru/snippets/07-action-bulk-delay/menu.conf b/examples/ru/snippets/07-action-bulk-delay/menu.conf new file mode 100644 index 0000000..efd21d2 --- /dev/null +++ b/examples/ru/snippets/07-action-bulk-delay/menu.conf @@ -0,0 +1,40 @@ +# Сниппет: обёртки действий bulk + delay +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/action-bulk-delay +# Открыть: /ame_snip_bulkdelay + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Bulk + Delay" +size: 1 +activators { command: "ame_snip_bulkdelay" } + +items: [ + { + slot: 4 + material: TNT + name: "&cОбратный отсчёт в несколько шагов" + lore: [ + "&7Кликните - получите серию сообщений" + "&7и звук в конце." + ] + click { + actions { + # bulk = список групп действий, выполняются по порядку + bulk: [ + { message: "&e3..." } + { message: "&62..." } + { message: "&c1..." } + ] + # delay = выполнить вложенный блок actions через N тиков + delay { + delay: 60 + actions { + sound: ${successSound} + message: "&aБум!" + } + } + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/07-action-bulk-delay/meta.json b/examples/ru/snippets/07-action-bulk-delay/meta.json new file mode 100644 index 0000000..cbef7d1 --- /dev/null +++ b/examples/ru/snippets/07-action-bulk-delay/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Действия: bulk + delay", + "category": "snippets", + "level": "intermediate", + "features": ["bulk", "delay", "action-wrappers"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_bulkdelay", + "order": 7 +} diff --git a/examples/ru/snippets/08-action-rand-actions/menu.conf b/examples/ru/snippets/08-action-rand-actions/menu.conf new file mode 100644 index 0000000..5ebd975 --- /dev/null +++ b/examples/ru/snippets/08-action-rand-actions/menu.conf @@ -0,0 +1,45 @@ +# Сниппет: randActions для одного из N случайных исходов +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/action-rand-actions +# Открыть: /ame_snip_rand + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Случайные действия" +size: 1 +activators { command: "ame_snip_rand" } + +items: [ + { + slot: 4 + material: ENDER_PEARL + name: "&5Бросок удачи" + lore: ["&7Клик - случайный исход из 4 вариантов."] + click { + actions { + # Случайно выбрать ровно одну из 4 записей и выполнить её блок. + randActions: [ + { + sound: ${successSound} + message: "&aДжекпот! +10 опыта" + giveXp: 10 + } + { + sound: ${successSound} + message: "&eНеплохо. +1 изумруд" + itemAdd { material: EMERALD } + } + { + sound: ${clickSound} + message: "&7Так себе. +5 жареной говядины" + itemAdd { material: COOKED_BEEF, count: 5 } + } + { + sound: ${failSound} + message: "&cПовезёт в следующий раз." + } + ] + } + } + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/08-action-rand-actions/meta.json b/examples/ru/snippets/08-action-rand-actions/meta.json new file mode 100644 index 0000000..b30adf7 --- /dev/null +++ b/examples/ru/snippets/08-action-rand-actions/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Действие: randActions", + "category": "snippets", + "level": "intermediate", + "features": ["randActions", "weighted-random-via-repeat"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_rand", + "order": 8 +} diff --git a/examples/ru/snippets/09-activator-clickitem/menu.conf b/examples/ru/snippets/09-activator-clickitem/menu.conf new file mode 100644 index 0000000..bfd15ec --- /dev/null +++ b/examples/ru/snippets/09-activator-clickitem/menu.conf @@ -0,0 +1,33 @@ +# Сниппет: активатор clickItem - открыть меню правым кликом по предмету в руке +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/activator-clickitem +# Открыть: возьмите NETHER_STAR с именем "Жезл меню" и кликните по нему правой кнопкой. +# (Для теста получите его командой: /give @s minecraft:nether_star{display:{Name:'{"text":"Жезл меню"}'}}) +# Требования: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Меню открыто кликом по предмету" +size: 1 + +activators { + clickItem { + material: NETHER_STAR + name: "&6Жезл меню" + } +} + +items: [ + { + slot: 4 + material: BOOK + name: "&aСработало" + lore: [ + "&7Вы открыли это меню правым кликом" + "&7по NETHER_STAR с именем &6Жезл меню&7 в руке." + "" + "&7Смотрите документацию активаторов: clickEntity, clickBlockType," + "&7clickNPC, regionJoin и другие." + ] + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/09-activator-clickitem/meta.json b/examples/ru/snippets/09-activator-clickitem/meta.json new file mode 100644 index 0000000..30ebf60 --- /dev/null +++ b/examples/ru/snippets/09-activator-clickitem/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Активатор: clickItem", + "category": "snippets", + "level": "intermediate", + "features": ["clickItem-activator", "no-command"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(ПКМ по предмету)", + "order": 9 +} diff --git a/examples/ru/snippets/10-template-substitution/menu.conf b/examples/ru/snippets/10-template-substitution/menu.conf new file mode 100644 index 0000000..66175fd --- /dev/null +++ b/examples/ru/snippets/10-template-substitution/menu.conf @@ -0,0 +1,38 @@ +# Сниппет: подстановка шаблона HOCON + переопределение +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/template-substitution +# Открыть: /ame_snip_template + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Подстановка шаблона" +size: 1 +activators { command: "ame_snip_template" } + +# Переиспользуемый шаблон предмета, определённый на уровне файла. +prizeTile { + material: NETHER_STAR + lore: ["&7Кликните за призом."] + click { + actions { + itemAdd { material: DIAMOND } + sound: ${successSound} + } + } +} + +items: [ + # Ссылаемся на шаблон, переопределяем slot + name. Блок click из prizeTile сохраняется. + ${prizeTile} { + slot: 2 + name: "&aОбычный приз" + } + ${prizeTile} { + slot: 4 + name: "&6Редкий приз" + } + ${prizeTile} { + slot: 6 + name: "&dЭпический приз" + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/snippets/10-template-substitution/meta.json b/examples/ru/snippets/10-template-substitution/meta.json new file mode 100644 index 0000000..a7dec7f --- /dev/null +++ b/examples/ru/snippets/10-template-substitution/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Подстановка шаблона HOCON", + "category": "snippets", + "level": "intermediate", + "features": ["template-substitution", "object-merge", "DRY-pattern"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_snip_template", + "order": 10 +} diff --git a/examples/ru/snippets/11-inventory-crafting/menu.conf b/examples/ru/snippets/11-inventory-crafting/menu.conf new file mode 100644 index 0000000..d84d169 --- /dev/null +++ b/examples/ru/snippets/11-inventory-crafting/menu.conf @@ -0,0 +1,102 @@ +# Инвентарный крафт - крафт предметов с расходом ингредиентов из инвентаря +# Документация: https://abstractmenus.github.io/docs/ru/examples/snippets/inventory-crafting +# Открыть: /ame_craft +# Требования: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Верстак охотника" +size: 1 + +activators { + command: "ame_craft" +} + +# Определения рецептов на уровне файла - блок click ссылается на них через ${...}. + +helmetRecipe { + output { + material: LEATHER_HELMET + name: "&aШляпа охотника" + enchantments { + durability: 2 + } + } + requirements: [ + { material: LEATHER, count: 5 } + ] +} + +chestplateRecipe { + output { + material: LEATHER_CHESTPLATE + name: "&aКуртка охотника" + enchantments { + durability: 2 + thorns: 1 + } + } + requirements: [ + { material: LEATHER, count: 8 } + { material: STRING, count: 1 } + { material: IRON_INGOT, count: 1 } + ] +} + +items: [ + ${helmetRecipe.output} { + slot: 2 + flags: HIDE_ATTRIBUTES + lore: [ + "&7Требуется:" + "&7- Кожа x5" + "" + "&aКлик для крафта" + ] + click { + rules { + inventoryItems: ${helmetRecipe.requirements} + } + actions { + itemRemove: ${helmetRecipe.requirements} + itemAdd: ${helmetRecipe.output} + sound: ${successSound} + message: "&aВы скрафтили Шляпу охотника!" + } + denyActions: ${denyNotEnoughItems} + } + } + + ${chestplateRecipe.output} { + slot: 6 + flags: HIDE_ATTRIBUTES + lore: [ + "&7Требуется:" + "&7- Кожа x8" + "&7- Нить x1" + "&7- Железный слиток x1" + "" + "&aКлик для крафта" + ] + click { + rules { + inventoryItems: ${chestplateRecipe.requirements} + } + actions { + itemRemove: ${chestplateRecipe.requirements} + itemAdd: ${chestplateRecipe.output} + sound: ${successSound} + message: "&aВы скрафтили Куртку охотника!" + } + denyActions: ${denyNotEnoughItems} + } + } + + ${buttonClose} { slot: 8 } +] + +# Локальное действие для случая нехватки ингредиентов +denyNotEnoughItems { + sound: ${deniedSound} + message: "&cУ вас недостаточно материалов." +} diff --git a/examples/ru/snippets/11-inventory-crafting/meta.json b/examples/ru/snippets/11-inventory-crafting/meta.json new file mode 100644 index 0000000..fc2e529 --- /dev/null +++ b/examples/ru/snippets/11-inventory-crafting/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Инвентарный крафт", + "category": "snippets", + "level": "intermediate", + "features": ["inventoryItems-rule", "itemRemove", "itemAdd", "template-substitution", "recipe-template"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_craft", + "order": 11 +} diff --git a/examples/ru/state-and-vars/01-counter-toggle/menu.conf b/examples/ru/state-and-vars/01-counter-toggle/menu.conf new file mode 100644 index 0000000..8da045e --- /dev/null +++ b/examples/ru/state-and-vars/01-counter-toggle/menu.conf @@ -0,0 +1,107 @@ +# Счётчик и тоггл - демо персональных переменных +# Docs: https://abstractmenus.github.io/docs/ru/examples/state-and-vars/counter-toggle +# Open: /ame_counter +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Счётчик и тоггл" +size: 3 + +activators { + command: "ame_counter" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # ========== Секция счётчика ========== + # Сброс счётчика + { + slot: 10 + material: BARRIER + name: "&cСбросить счётчик" + lore: ["&7Клик - очистить счётчик."] + click { + actions { + removeVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Уменьшить + { + slot: 11 + material: RED_WOOL + name: "&c-1" + click { + actions { + decVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Отображение счётчика - по умолчанию 0, если переменная не задана + { + slot: 12 + material: BOOK + name: "&6Счётчик: &e%varp_:ame_counter:0%" + lore: ["&7Твой персональный счётчик, сохраняется между сессиями."] + } + + # Увеличить + { + slot: 13 + material: GREEN_WOOL + name: "&a+1" + click { + actions { + incVarp: "ame_counter" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # ========== Секция тоггла ========== + # Паттерн dual-item: первый предмет рендерится, когда переменная есть, + # второй - когда её нет. + + # Состояние ON (видно только когда переменная задана) + { + slot: 15 + material: LIME_DYE + name: "&aТоггл: ВКЛ" + lore: ["&7Клик - выключить."] + rules { existVarp: "ame_toggle" } + click { + actions { + removeVarp: "ame_toggle" + sound: ${clickSound} + refreshMenu: true + } + } + } + + # Состояние OFF (фолбек, без правил - рендерится, когда вариант ON скрыт) + { + slot: 15 + material: GRAY_DYE + name: "&7Тоггл: ВЫКЛ" + lore: ["&7Клик - включить."] + click { + actions { + setVarp: "ame_toggle::1" + sound: ${clickSound} + refreshMenu: true + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/state-and-vars/01-counter-toggle/meta.json b/examples/ru/state-and-vars/01-counter-toggle/meta.json new file mode 100644 index 0000000..9ede6d4 --- /dev/null +++ b/examples/ru/state-and-vars/01-counter-toggle/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Счётчик и тоггл", + "category": "state-and-vars", + "level": "intermediate", + "features": ["incVarp", "decVarp", "setVarp", "removeVarp", "existVarp", "refreshMenu", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_counter", + "order": 1 +} diff --git a/examples/ru/state-and-vars/02-vote-cooldown/menu.conf b/examples/ru/state-and-vars/02-vote-cooldown/menu.conf new file mode 100644 index 0000000..4fb01ec --- /dev/null +++ b/examples/ru/state-and-vars/02-vote-cooldown/menu.conf @@ -0,0 +1,79 @@ +# Кулдаун на голосование - глобальный счётчик голосов + персональный 24ч кулдаун +# Docs: https://abstractmenus.github.io/docs/ru/examples/state-and-vars/vote-cooldown +# Open: /ame_vote +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Ежедневное голосование" +size: 3 +updateInterval: 20 + +activators { + command: "ame_vote" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Глобальный счётчик голосов - сколько всего голосов получил сервер. + { + slot: 11 + material: BEACON + name: "&6Всего голосов на сервере" + lore: ["", "&e%var_:ame_vote_total:0%", "", "&8(За всё время, по всем игрокам)"] + } + + # ========== Кнопка голосования - dual-вариант ========== + # Вариант ON COOLDOWN: персональная временная переменная задана, голосовать пока нельзя. + { + slot: 13 + material: GRAY_DYE + name: "&7Голосовать (на кулдауне)" + lore: [ + "&7Ты уже голосовал недавно." + "" + "&cДоступно через &e%varpt_:ame_vote_cd%" + ] + rules { existVarp: "ame_vote_cd" } + } + # Вариант READY: кулдауна нет, клик - голосуем. + { + slot: 13 + material: LIME_DYE + name: "&aГолосовать!" + lore: [ + "&7Клик - проголосовать за сервер." + "&7Голосовать можно раз в 24 часа." + ] + click { + actions { + # Увеличиваем глобальный счётчик голосов. + incVar: "ame_vote_total" + # Ставим персональный кулдаун, который сам истечёт через 1 день. + setVarp: "ame_vote_cd::1::1d" + sound: ${successSound} + message: "&aСпасибо за голос! Возвращайся через 24 часа." + refreshMenu: true + } + } + } + + # Информация о наградах + { + slot: 15 + material: DIAMOND + name: "&bНаграды за голос" + lore: [ + "&7Каждый голос даёт:" + "&7- &f$100" + "&7- &f1 vote-токен" + "&7- &fшанс на алмазы" + "" + "&8Настраивается в твоём vote-обработчике" + ] + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/state-and-vars/02-vote-cooldown/meta.json b/examples/ru/state-and-vars/02-vote-cooldown/meta.json new file mode 100644 index 0000000..e10af6d --- /dev/null +++ b/examples/ru/state-and-vars/02-vote-cooldown/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Кулдаун на голосование", + "category": "state-and-vars", + "level": "intermediate", + "features": ["incVar-global", "setVarp-temporal", "existVarp", "varpt-placeholder", "var-placeholder", "dual-item-display"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_vote", + "order": 2 +} diff --git a/examples/ru/state-and-vars/03-global-stats-board/menu.conf b/examples/ru/state-and-vars/03-global-stats-board/menu.conf new file mode 100644 index 0000000..a34b87b --- /dev/null +++ b/examples/ru/state-and-vars/03-global-stats-board/menu.conf @@ -0,0 +1,73 @@ +# Доска статов - живой вывод статистики игрока и сервера +# Docs: https://abstractmenus.github.io/docs/ru/examples/state-and-vars/global-stats-board +# Open: /ame_stats +# Requires: PlaceholderAPI + statistic expansion + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6&lСтатистика игрока %player_name%" +size: 4 +updateInterval: 20 + +activators { + command: "ame_stats" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "27-35" } + + # ========== Секция игрока (ряд 1) ========== + { + slot: 10 + texture: "ebfe9b7af68dc1264a6fc969f3d7b08e50e61e6ee91cea83daabd18820f0ef" + name: "&6Убийств игроков" + lore: ["", "&e%statistic_player_kills%"] + } + { + slot: 12 + texture: "1ae3855f952cd4a03c148a946e3f812a5955ad35cbcb52627ea4acd47d3081" + name: "&6Смертей" + lore: ["", "&c%statistic_deaths%"] + } + { + slot: 14 + texture: "f67b27fb7e29ec98e1cd4a8f8466856d9ef3f2e9fbd9aed6311f8abe54b6ab2" + name: "&6Убийств мобов" + lore: ["", "&e%statistic_mob_kills%"] + } + { + slot: 16 + texture: "f32c6171532a2a87f0eeb28edd010833f33f0ae6841a524e1b5200a35d385050" + name: "&6Часов в игре" + lore: ["", "&e%statistic_hours_played%ч"] + } + + # ========== Секция сервера (ряд 2) ========== + { + slot: 19 + material: BEACON + name: "&aОнлайн сейчас" + lore: ["", "&e%server_online%&7 / &e%server_max_players%"] + } + { + slot: 21 + material: CLOCK + name: "&aВремя сервера" + lore: ["", "&e%server_time_HH:mm:ss%"] + } + { + slot: 23 + material: ENDER_CHEST + name: "&aTPS" + lore: ["", "&e%server_tps_1%&7, &e%server_tps_5%&7, &e%server_tps_15%"] + } + { + slot: 25 + material: COMPASS + name: "&aМир" + lore: ["", "&e%player_world%"] + } + + ${buttonClose} { slot: 31 } +] diff --git a/examples/ru/state-and-vars/03-global-stats-board/meta.json b/examples/ru/state-and-vars/03-global-stats-board/meta.json new file mode 100644 index 0000000..0cb5e56 --- /dev/null +++ b/examples/ru/state-and-vars/03-global-stats-board/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Доска статов", + "category": "state-and-vars", + "level": "intermediate", + "features": ["updateInterval", "placeholders", "statistic-expansion", "server-expansion", "PLAYER_HEAD-texture"], + "dependencies": ["PlaceholderAPI"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_stats", + "order": 3 +} diff --git a/examples/ru/state-and-vars/04-bungee-status/menu.conf b/examples/ru/state-and-vars/04-bungee-status/menu.conf new file mode 100644 index 0000000..585a69e --- /dev/null +++ b/examples/ru/state-and-vars/04-bungee-status/menu.conf @@ -0,0 +1,90 @@ +# Bungee Status - выбор сервера с живым онлайном через BungeeCord +# Docs: https://abstractmenus.github.io/docs/ru/examples/state-and-vars/bungee-status +# Open: /ame_servers +# Requires: BungeeCord-прокси (или Velocity) с именованными бэкендами + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Выбор сервера" +size: 3 +updateInterval: 40 + +activators { + command: "ame_servers" +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Лобби + { + slot: 10 + material: BOOKSHELF + name: "&e&lЛобби" + lore: [ + "&7Главный хаб для выбора режимов." + "" + "&7Онлайн: &e%bungee_lobby%" + "" + "&a> Клик - подключиться" + ] + glow: true + click { + bungeeConnect: "lobby" + } + } + + # Survival + { + slot: 12 + material: GRASS_BLOCK + name: "&e&lSurvival" + lore: [ + "&7Ванильное выживание." + "" + "&7Онлайн: &e%bungee_survival%" + "" + "&a> Клик - подключиться" + ] + click { + bungeeConnect: "survival" + } + } + + # SkyWars + { + slot: 14 + material: ENDER_EYE + name: "&e&lSky Wars" + lore: [ + "&7PvP-миниигра на островах." + "" + "&7Онлайн: &e%bungee_sw%" + "" + "&a> Клик - подключиться" + ] + click { + bungeeConnect: "sw" + } + } + + # Build Battle + { + slot: 16 + material: BRICKS + name: "&e&lBuild Battle" + lore: [ + "&7Тематические соревнования по постройкам." + "" + "&7Онлайн: &e%bungee_bb%" + "" + "&a> Клик - подключиться" + ] + click { + bungeeConnect: "bb" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/state-and-vars/04-bungee-status/meta.json b/examples/ru/state-and-vars/04-bungee-status/meta.json new file mode 100644 index 0000000..61a3b58 --- /dev/null +++ b/examples/ru/state-and-vars/04-bungee-status/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Bungee Status", + "category": "state-and-vars", + "level": "intermediate", + "features": ["bungeeConnect", "bungee-placeholders", "updateInterval", "glow"], + "dependencies": ["BungeeCord"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_servers", + "order": 4 +} diff --git a/examples/ru/state-and-vars/05-item-enhancer/menu.conf b/examples/ru/state-and-vars/05-item-enhancer/menu.conf new file mode 100644 index 0000000..21abfef --- /dev/null +++ b/examples/ru/state-and-vars/05-item-enhancer/menu.conf @@ -0,0 +1,101 @@ +# Энхансер предметов - drag-and-drop с условной трансформацией через правило oneof +# Docs: https://abstractmenus.github.io/docs/ru/examples/state-and-vars/item-enhancer +# Open: /ame_enhancer +# Requires: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Энхансер предметов" +size: 1 + +activators { + command: "ame_enhancer" +} + +# Слоты 2 (вход) и 6 (результат) - drag-and-drop. Слот 4 - кнопка триггера. +draggable: [ + "--x---x--" +] + +# Заглушка для слота результата при открытии меню или невалидном вводе. +resultStub { + slot: 6 + material: WHITE_STAINED_GLASS_PANE + name: "&7Результат" +} + +# Переиспользуемые блоки +inputSlotRule { slot: 2 } +outputAction { slot: 6, count: "%changed_item_amount%" } + +# Таблица трансформаций: каждая запись связывает правило "положенный предмет совпадает с X" +# с действием, которое выдаёт результат Y. `oneof` короткозамыкается на первом совпадении - +# выигрывает первая подходящая трансформация. +enhanceRules { + oneof: [ + { + placedItem: ${inputSlotRule} { material: STONE } + actions { placeItem: ${outputAction} { material: COAL_ORE } } + } + { + placedItem: ${inputSlotRule} { material: APPLE } + actions { placeItem: ${outputAction} { material: GOLDEN_APPLE } } + } + { + placedItem: ${inputSlotRule} { material: WOODEN_SWORD } + actions { placeItem: ${outputAction} { material: DIAMOND_SWORD } } + } + { + placedItem: ${inputSlotRule} { material: WOODEN_AXE } + actions { placeItem: ${outputAction} { material: DIAMOND_AXE } } + } + ] +} + +items: [ + # Декоративные стёкла вокруг drag-and-drop слотов + { + slot: [ "xx-x-x-x-" ] + material: BLACK_STAINED_GLASS_PANE + name: " " + } + + # Заглушка слота результата по умолчанию + ${resultStub} + + # Карточка с рецептами + { + slot: 8 + material: NAME_TAG + name: "&aРецепты" + lore: [ + "&fКамень &7-> &bУгольная руда" + "&fЯблоко &7-> &bЗолотое яблоко" + "&fДеревянный меч &7-> &bАлмазный меч" + "&fДеревянный топор &7-> &bАлмазный топор" + ] + } + + # Кнопка "Улучшить!" + { + slot: 4 + material: NETHER_STAR + name: "&bУлучшить!" + click { + rules: ${enhanceRules} + actions { + removePlaced: 2 + sound: ${successSound} + } + denyActions { + rules { + placedItem { slot: 6, material: AIR } + } + actions { + setButton: ${resultStub} + sound: ${deniedSound} + } + } + } + } +] diff --git a/examples/ru/state-and-vars/05-item-enhancer/meta.json b/examples/ru/state-and-vars/05-item-enhancer/meta.json new file mode 100644 index 0000000..590a27c --- /dev/null +++ b/examples/ru/state-and-vars/05-item-enhancer/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Энхансер предметов", + "category": "state-and-vars", + "level": "advanced", + "features": ["draggable-slots", "placedItem-rule", "placeItem", "removePlaced", "setButton", "oneof-rule", "conditional-transformation"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "ame_enhancer", + "order": 5 +} diff --git a/examples/ru/world-integrations/01-region-welcome/menu.conf b/examples/ru/world-integrations/01-region-welcome/menu.conf new file mode 100644 index 0000000..81b02ab --- /dev/null +++ b/examples/ru/world-integrations/01-region-welcome/menu.conf @@ -0,0 +1,61 @@ +# Приветствие региона - встречаем игроков, входящих в регион WorldGuard +# Документация: https://abstractmenus.github.io/docs/ru/examples/world-integrations/region-welcome +# Открыть: автоматически при входе в регион "spawn" +# Требует: WorldGuard + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6Добро пожаловать на спавн" +size: 3 + +activators { + regionJoin: ["spawn"] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Приветственный баннер + { + slot: 4 + material: NETHER_STAR + name: "&6&lДобро пожаловать на MyTestServer" + lore: [ + "&7Вы вошли в регион &fSpawn&7." + "" + "&7Продолжайте с того места, где остановились." + ] + } + + # Быстрые ссылки + { + slot: 11 + material: COMPASS + name: "&aОткрыть главный хаб" + lore: ["&7Просмотреть все доступные меню."] + click { + openMenu: "ame_hub" + } + } + { + slot: 13 + material: WRITTEN_BOOK + name: "&aПравила сервера" + lore: ["&7Прочитайте правила перед игрой."] + click { + openMenu: "ame_rules" + } + } + { + slot: 15 + material: CHEST + name: "&aПолучить стартовый кит" + lore: ["&7Бесплатные предметы для новых игроков."] + click { + openMenu: "ame_kit" + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/world-integrations/01-region-welcome/meta.json b/examples/ru/world-integrations/01-region-welcome/meta.json new file mode 100644 index 0000000..3aba0f3 --- /dev/null +++ b/examples/ru/world-integrations/01-region-welcome/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Приветствие региона", + "category": "world-integrations", + "level": "intermediate", + "features": ["regionJoin", "openMenu", "WorldGuard-integration"], + "dependencies": ["WorldGuard"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(вход в регион)", + "order": 1 +} diff --git a/examples/ru/world-integrations/02-npc-click-info/menu.conf b/examples/ru/world-integrations/02-npc-click-info/menu.conf new file mode 100644 index 0000000..10f1f15 --- /dev/null +++ b/examples/ru/world-integrations/02-npc-click-info/menu.conf @@ -0,0 +1,73 @@ +# Клик по NPC - информационное меню о Citizens NPC, на которого кликнул игрок +# Документация: https://abstractmenus.github.io/docs/ru/examples/world-integrations/npc-click-info +# Открыть: правый клик по любому Citizens NPC с id 1, 2 или 3 +# Требует: Citizens + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&6NPC: %activator_npc_name%" +size: 3 + +activators { + clickNPC: [1, 2, 3] +} + +items: [ + ${borderBlack} { slot: "0-8" } + ${borderBlack} { slot: "18-26" } + + # Заголовок берёт данные из контекста NPC + { + slot: 4 + material: PLAYER_HEAD + skullOwner: "%activator_npc_name%" + name: "&6%activator_npc_name%" + lore: [ + "&7Citizens NPC #&f%activator_npc_id%" + "&7Мир: &f%activator_npc_world%" + "" + "&7Кликните по любой плитке ниже для действия." + ] + } + + # Универсальные действия для любого клика по NPC + { + slot: 11 + material: PAPER + name: "&aПоздороваться" + lore: ["&7Заставить этого NPC поздороваться."] + click { + actions { + message: "&e[%activator_npc_name%] &fПривет, %player_name%!" + sound: ${clickSound} + } + } + } + { + slot: 13 + material: COMPASS + name: "&aПоказать местоположение" + lore: ["&7Вывести координаты этого NPC."] + click { + actions { + message: "&e[%activator_npc_name%] &fЯ нахожусь в %activator_npc_world% у NPC с id %activator_npc_id%." + sound: ${clickSound} + } + } + } + { + slot: 15 + material: EMERALD + name: "&aПоговорить с торговцем" + lore: ["&7Открыть магазин этого NPC, если он есть."] + click { + actions { + # В реальной настройке вы бы направляли конкретных NPC в конкретные меню магазинов. + # Демо просто открывает обобщённый магазин торговца. + openMenu: "ame_shop" + } + } + } + + ${buttonClose} { slot: 22 } +] diff --git a/examples/ru/world-integrations/02-npc-click-info/meta.json b/examples/ru/world-integrations/02-npc-click-info/meta.json new file mode 100644 index 0000000..12fcba3 --- /dev/null +++ b/examples/ru/world-integrations/02-npc-click-info/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Клик по NPC", + "category": "world-integrations", + "level": "intermediate", + "features": ["clickNPC-multiple-ids", "%activator_npc_*%-placeholders", "skullOwner-from-placeholder", "openMenu-routing"], + "dependencies": ["Citizens"], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(ПКМ по NPC)", + "order": 2 +} diff --git a/examples/ru/world-integrations/03-block-as-button/menu.conf b/examples/ru/world-integrations/03-block-as-button/menu.conf new file mode 100644 index 0000000..0f2bbf7 --- /dev/null +++ b/examples/ru/world-integrations/03-block-as-button/menu.conf @@ -0,0 +1,29 @@ +# Блок-как-кнопка - активатор clickBlockType открывает меню при клике игрока по материалу +# Документация: https://abstractmenus.github.io/docs/ru/examples/world-integrations/block-as-button +# Открыть: правый клик по любому DIAMOND_BLOCK в мире +# Требует: - + +include required(file("./plugins/AbstractMenus/menus/example/_shared/templates.conf")) + +title: "&bАлмазный блок нажат на %activator_block_x%, %activator_block_y%, %activator_block_z%" +size: 1 + +activators { + clickBlockType: ["DIAMOND_BLOCK"] +} + +items: [ + { + slot: 4 + material: DIAMOND + name: "&bПриветствие" + lore: [ + "&7Вы кликнули по алмазному блоку в" + "&7мире &f%activator_block_world%&7." + "" + "&7Плейсхолдеры активатора дают вам" + "&7координаты блока, мир, тип и т.д." + ] + } + ${buttonClose} { slot: 8 } +] diff --git a/examples/ru/world-integrations/03-block-as-button/meta.json b/examples/ru/world-integrations/03-block-as-button/meta.json new file mode 100644 index 0000000..bf54a87 --- /dev/null +++ b/examples/ru/world-integrations/03-block-as-button/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Блок как кнопка", + "category": "world-integrations", + "level": "intermediate", + "features": ["clickBlockType-activator", "activator_block-placeholders", "no-command"], + "dependencies": [], + "min_plugin_version": "2.0.0", + "min_minecraft_version": "1.21", + "uses_shared_templates": true, + "command": "(ПКМ по блоку)", + "order": 3 +} diff --git a/package-lock.json b/package-lock.json index c91fa71..a34dfcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@astrojs/mdx": "^5.0.4", "@astrojs/starlight": "^0.38.4", "astro": "^6.1.9", + "jszip": "^3.10.1", "sharp": "^0.34.4", "typescript": "^5.7.3" } @@ -2651,6 +2652,12 @@ "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/crossws": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", @@ -3753,6 +3760,18 @@ "@babel/runtime": "^7.23.2" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", @@ -3896,6 +3915,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -3920,6 +3945,18 @@ "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", "license": "MIT" }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -3938,6 +3975,15 @@ "node": ">= 8" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -5269,6 +5315,12 @@ "@pagefind/windows-x64": "1.5.2" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -5444,6 +5496,12 @@ "node": ">=6" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -5460,6 +5518,21 @@ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -5903,6 +5976,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/sax": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", @@ -5924,6 +6003,12 @@ "node": ">=10" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", @@ -6058,6 +6143,15 @@ "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index 4721b38..2fd8bce 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@astrojs/mdx": "^5.0.4", "@astrojs/starlight": "^0.38.4", "astro": "^6.1.9", + "jszip": "^3.10.1", "sharp": "^0.34.4", "typescript": "^5.7.3" } diff --git a/src/components/CategoryIndex.astro b/src/components/CategoryIndex.astro new file mode 100644 index 0000000..8c0f77d --- /dev/null +++ b/src/components/CategoryIndex.astro @@ -0,0 +1,121 @@ +--- +// CategoryIndex.astro +// Auto-generates a table of every menu in a category for category landing pages. +// Reads all meta.json files for the locale, filters by the given category, +// renders a table with name (linked), level, features, requires, command. + +interface Meta { + title: string; + category: string; + level: 'beginner' | 'intermediate' | 'advanced'; + features: string[]; + dependencies: string[]; + command: string; + order: number; +} + +interface Props { + category: string; + lang: 'en' | 'ru'; +} + +const { category, lang } = Astro.props; + +const metaFiles = import.meta.glob('/examples/**/meta.json', { + import: 'default', + eager: true, +}) as Record; + +const menus = Object.entries(metaFiles) + .filter(([path]) => path.includes(`/examples/${lang}/`)) + .map(([path, meta]) => { + const match = path.match(/\/examples\/[^/]+\/(.+)\/meta\.json$/); + const menuPath = match ? match[1] : ''; + return { menuPath, meta }; + }) + .filter((m) => m.meta.category === category) + .sort((a, b) => (a.meta.order ?? 0) - (b.meta.order ?? 0)); + +// menuPath is like "shops/01-cake-shop". Extract the slug for the docs URL: +// the docs page is /docs//examples///. +// docsSlug is just the menu folder name minus the leading "NN-" digits. +function pathToDocsSlug(menuPath: string): string { + const last = menuPath.split('/').pop() ?? ''; + return last.replace(/^\d+-/, ''); +} + +// i18n. See backlog: i18n centralization for the wiki. +const t = { + en: { + name: 'Name', + level: 'Level', + features: 'Features', + requires: 'Requires', + command: 'Command', + levels: { beginner: 'beginner', intermediate: 'intermediate', advanced: 'advanced' }, + empty: 'No menus in this category yet.', + }, + ru: { + name: 'Имя', + level: 'Уровень', + features: 'Фичи', + requires: 'Требует', + command: 'Команда', + levels: { beginner: 'новичок', intermediate: 'средний', advanced: 'продвинутый' }, + empty: 'В этой категории пока нет меню.', + }, +}[lang]; + +// Detect command-style activator vs descriptive (clickItem/region/etc.) +function isCommand(cmd: string): boolean { + return !cmd.startsWith('('); +} +--- + + + + + + + + + + + + + {menus.map((m) => ( + + + + + + + + ))} + +
{t.name}{t.level}{t.features}{t.requires}{t.command}
+ + {m.meta.title} + + + {t.levels[m.meta.level]} + + {m.meta.features.slice(0, 4).map((f) => ( + {f} + ))} + {m.meta.features.length > 4 && +{m.meta.features.length - 4}} + + {m.meta.dependencies.length === 0 ? ( + - + ) : ( + m.meta.dependencies.map((d) => {d}) + )} + + {isCommand(m.meta.command) ? ( + /{m.meta.command} + ) : ( + {m.meta.command} + )} +
+ +{menus.length === 0 &&

{t.empty}

} diff --git a/src/components/DownloadCard.astro b/src/components/DownloadCard.astro new file mode 100644 index 0000000..3921f8b --- /dev/null +++ b/src/components/DownloadCard.astro @@ -0,0 +1,173 @@ +--- +// DownloadCard.astro +// Two download buttons: single menu.conf and full bundle (.zip via JSZip). +// Bundle includes: menu.conf, local templates.conf (if exists), _shared/templates.conf (if uses_shared_templates), INSTALL.txt. + +interface Meta { + uses_shared_templates: boolean; + command: string; + title: string; +} + +interface Props { + path: string; // e.g. "shops/01-cake-shop" + lang: 'en' | 'ru'; + meta: Meta; + hasLocalTemplates: boolean; +} + +const { path, lang, meta, hasLocalTemplates } = Astro.props; + +// Use ?url import so Vite bundles the file into dist/_astro/ with a hashed name. +// Without this, the literal `/examples/...` URL would 404 in production +// because examples/ isn't under public/. +const menuFileUrls = import.meta.glob('/examples/**/menu.conf', { + query: '?url', + import: 'default', + eager: true, +}) as Record; + +const menuKey = `/examples/${lang}/${path}/menu.conf`; +const menuConfUrl = menuFileUrls[menuKey]; +if (!menuConfUrl) { + throw new Error(`DownloadCard: menu.conf URL not resolved for ${menuKey}`); +} + +const docsLinkBase = `https://abstractmenus.github.io/docs/${lang}/examples`; + +// i18n. See backlog: i18n centralization for the wiki. +const t = { + en: { + bundleBtn: '↓ Bundle (.zip)', + bundleIncludesShared: 'Bundle includes _shared/templates.conf and INSTALL.txt.', + bundleIncludesPlain: 'Bundle includes menu.conf and INSTALL.txt.', + }, + ru: { + bundleBtn: '↓ Бандл (.zip)', + bundleIncludesShared: 'Бандл включает _shared/templates.conf и INSTALL.txt.', + bundleIncludesPlain: 'Бандл включает menu.conf и INSTALL.txt.', + }, +}[lang]; +--- + +
+
+ + ↓ menu.conf + + +
+

+ {meta.uses_shared_templates ? t.bundleIncludesShared : t.bundleIncludesPlain} +

+
+ + diff --git a/src/components/MenuExample.astro b/src/components/MenuExample.astro new file mode 100644 index 0000000..0809385 --- /dev/null +++ b/src/components/MenuExample.astro @@ -0,0 +1,83 @@ +--- +// MenuExample.astro +// Renders a complete menu example: badges, code (in tabs when multiple files), download buttons. +// Inputs: path (relative to examples//), lang, optional flags. + +import { Code, Tabs, TabItem } from '@astrojs/starlight/components'; +import MenuMeta from './MenuMeta.astro'; +import DownloadCard from './DownloadCard.astro'; + +interface Props { + path: string; // e.g. "shops/01-cake-shop" + lang: 'en' | 'ru'; // locale + showShared?: boolean; + showTemplates?: boolean; +} + +const { path, lang, showShared = false, showTemplates = false } = Astro.props; + +// Eager-load all conf files and meta.json files +const confFiles = import.meta.glob('/examples/**/*.conf', { + query: '?raw', + import: 'default', + eager: true, +}) as Record; + +const metaFiles = import.meta.glob('/examples/**/meta.json', { + import: 'default', + eager: true, +}) as Record; + +// Resolve specific files for this example +const menuKey = `/examples/${lang}/${path}/menu.conf`; +const metaKey = `/examples/${lang}/${path}/meta.json`; +const localTemplatesKey = `/examples/${lang}/${path}/templates.conf`; +const sharedKey = `/examples/${lang}/_shared/templates.conf`; + +const menuCode = confFiles[menuKey]; +const meta = metaFiles[metaKey]; +const localTemplatesCode = confFiles[localTemplatesKey] ?? null; +const sharedCode = confFiles[sharedKey] ?? null; + +if (!menuCode) { + throw new Error(`MenuExample: menu.conf not found at ${menuKey}`); +} +if (!meta) { + throw new Error(`MenuExample: meta.json not found at ${metaKey}`); +} + +const includeLocal = showTemplates && localTemplatesCode !== null; +const includeShared = showShared && meta.uses_shared_templates && sharedCode !== null; +const useTabs = includeLocal || includeShared; +--- + + diff --git a/src/components/MenuMeta.astro b/src/components/MenuMeta.astro new file mode 100644 index 0000000..17b6cb7 --- /dev/null +++ b/src/components/MenuMeta.astro @@ -0,0 +1,134 @@ +--- +// MenuMeta.astro +// Renders metadata badges for an example menu. +// Inputs: meta object + lang for label translation. + +interface Meta { + title: string; + category: string; + level: 'beginner' | 'intermediate' | 'advanced'; + features: string[]; + dependencies: string[]; + min_plugin_version: string; + min_minecraft_version: string; + uses_shared_templates: boolean; + command: string; + order: number; +} + +interface Props { + meta: Meta; + lang: 'en' | 'ru'; +} + +const { meta, lang } = Astro.props; + +// i18n string tables. See backlog: i18n centralization for the wiki. +const t = { + en: { + levels: { beginner: 'beginner', intermediate: 'intermediate', advanced: 'advanced' }, + features: 'Features:', + requires: 'Requires:', + openWith: 'Open:', + copy: 'Copy', + copied: 'Copied', + }, + ru: { + levels: { beginner: 'новичок', intermediate: 'средний', advanced: 'продвинутый' }, + features: 'Фичи:', + requires: 'Требует:', + openWith: 'Открыть:', + copy: 'Скопировать', + copied: 'Скопировано', + }, +}[lang]; + +// Render `/name` for actual command activators, or a plain descriptive hint for +// menus opened via clickItem / regionJoin / clickNPC etc. (e.g. "(right-click NPC)") +const isCommand = !meta.command.startsWith('('); +--- + + + + diff --git a/src/components/PackBuilder.astro b/src/components/PackBuilder.astro new file mode 100644 index 0000000..1a41736 --- /dev/null +++ b/src/components/PackBuilder.astro @@ -0,0 +1,375 @@ +--- +// PackBuilder.astro +// Lets the user pick any subset of example menus and build a custom zip bundle. +// Reuses the JSZip pattern from DownloadCard but with multi-select UI. + +interface Meta { + title: string; + category: string; + level: 'beginner' | 'intermediate' | 'advanced'; + features: string[]; + dependencies: string[]; + min_plugin_version: string; + min_minecraft_version: string; + uses_shared_templates: boolean; + command: string; + order: number; +} + +interface Props { + lang: 'en' | 'ru'; +} + +const { lang } = Astro.props; + +// Load every meta.json across both locales, then filter. +const metaFiles = import.meta.glob('/examples/**/meta.json', { + import: 'default', + eager: true, +}) as Record; + +const menus = Object.entries(metaFiles) + .filter(([path]) => path.includes(`/examples/${lang}/`)) + .map(([path, meta]) => { + // /examples/en/shops/01-cake-shop/meta.json -> path: shops/01-cake-shop + const match = path.match(/\/examples\/[^/]+\/(.+)\/meta\.json$/); + const menuPath = match ? match[1] : ''; + return { menuPath, meta }; + }) + .filter((m) => m.menuPath !== ''); + +// Group by category +type Entry = (typeof menus)[number]; +const byCategory: Record = {}; +for (const m of menus) { + const cat = m.meta.category; + if (!byCategory[cat]) byCategory[cat] = []; + byCategory[cat].push(m); +} +for (const cat in byCategory) { + byCategory[cat].sort((a, b) => (a.meta.order ?? 0) - (b.meta.order ?? 0)); +} + +// Stable display order for categories +const categoryOrder = [ + 'shops', + 'hub-and-nav', + 'cosmetics', + 'donate', + 'casino-and-games', + 'kits-and-rewards', + 'admin-tools', + 'info-pages', + 'state-and-vars', + 'world-integrations', + 'snippets', +]; +const sortedCategories = categoryOrder.filter((c) => byCategory[c]); + +const totalCount = menus.length; + +// i18n. See backlog: i18n centralization for the wiki. +const t = { + en: { + selectAll: 'Select all', + selectNone: 'Select none', + buildBtn: '↓ Build pack (.zip)', + summary: (sel: number, tot: number) => `${sel} / ${tot} selected`, + needsAtLeastOne: 'Select at least one menu first.', + levels: { beginner: 'beginner', intermediate: 'intermediate', advanced: 'advanced' }, + expandAll: 'Expand all', + collapseAll: 'Collapse all', + }, + ru: { + selectAll: 'Выбрать все', + selectNone: 'Снять всё', + buildBtn: '↓ Собрать бандл (.zip)', + summary: (sel: number, tot: number) => `выбрано ${sel} из ${tot}`, + needsAtLeastOne: 'Сначала выбери хотя бы одно меню.', + levels: { beginner: 'новичок', intermediate: 'средний', advanced: 'продвинутый' }, + expandAll: 'Развернуть все', + collapseAll: 'Свернуть все', + }, +}[lang]; + +// Pre-render summary template strings the client script will reuse. +const summaryEmpty = t.summary(0, totalCount); +const summaryFull = t.summary(totalCount, totalCount); +--- + +
+
+ + + + {summaryEmpty} + +
+ + {sortedCategories.map((cat) => ( +
+ + + + {cat} + {byCategory[cat].length} + +
    + {byCategory[cat].map((m) => ( +
  • + +
  • + ))} +
+
+ ))} +
+ + diff --git a/src/content/docs/en/examples/admin-tools/index.mdx b/src/content/docs/en/examples/admin-tools/index.mdx new file mode 100644 index 0000000..1356b4c --- /dev/null +++ b/src/content/docs/en/examples/admin-tools/index.mdx @@ -0,0 +1,18 @@ +--- +title: Admin Tools +description: Online-player browsers and quick moderation menus, gated by permission. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Online-player browsers and quick moderation menus, gated by permission. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/admin-tools/online-players.mdx b/src/content/docs/en/examples/admin-tools/online-players.mdx new file mode 100644 index 0000000..401129c --- /dev/null +++ b/src/content/docs/en/examples/admin-tools/online-players.mdx @@ -0,0 +1,99 @@ +--- +title: Online Players +description: Generated menu over the PLAYERS catalog. Browse online players as heads, left-click to teleport, shift-click to kick. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The headline use case for `GeneratedMenu`: a paginated browser of online players. Each head is auto-generated from the `PLAYERS` catalog with health and world info in the lore. Left-click teleports admin to the player, shift-click kicks them. Restricted to admins via the `rulesAdmin` shared snippet. + +## What it teaches + +- Building a generated menu with a catalog + matrix + templates +- Using catalog placeholders (`%ctg_player_name%`, `%ctg_player_health%`, `%ctg_player_world%`) +- Click-type discrimination: `click { left { ... } shift_left { ... } }` +- The `command` action with `console:` to run server commands as console +- Built-in pagination via `pageNext` / `pagePrev` (shared `${buttonNext}` and `${buttonPrev}`) +- Pagination placeholders: `%page%`, `%pages_count%`, `%pages_total%` +- Menu-level access restriction: top-level `rules: ${rulesAdmin}` + + + +## How GeneratedMenu pieces fit together + +Three blocks make a generated menu: + +```hocon +catalog { type: PLAYERS } # the source of dynamic content + +matrix { # how to lay it out + cells: [ "_________", "_xxxxxxx_", ... ] + templates { + "x" { ... item template ... } + } +} + +items: [ # static items (borders, buttons, page counter) + ... +] +``` + +The catalog produces a stream of objects. The matrix decides where each catalog entry goes (`x` cells consume catalog entries one by one). Static items in `items:` render on every page in the same positions. + +## Catalog placeholders + +Inside matrix templates, the current catalog object is exposed via `%ctg_%`. For the `PLAYERS` catalog, the player object has `name`, `health`, `food_level`, `world`, `gamemode`, and a few others. Different catalog types expose different keys - see the [Generation docs](/docs/en/advanced/generation/) for a full table. + +## Click types + +The `click` block can specify per-button-type handlers: + +- **`left { ... }`** - left-click only +- **`right { ... }`** - right-click only +- **`shift_left { ... }`** - shift + left-click +- **`shift_right { ... }`** - shift + right-click +- **`drop { ... }`** - drop key (Q) + +If only one click variant is defined, other clicks are no-ops. This menu uses `left` for teleport and `shift_left` for kick - so a regular shift-left click won't accidentally teleport. + +## Pagination + +Generated menus paginate automatically when the catalog has more entries than fit. The `pageNext` and `pagePrev` actions move between pages. The shared `${buttonNext}` and `${buttonPrev}` templates wire those actions to ARROW items. + +If the menu fits on one page (catalog has fewer entries than matrix slots), the next/prev buttons still render but their actions are no-ops. + +The `%page%`, `%pages_count%`, and `%pages_total%` placeholders show pagination state - useful for "Page 2 / 5" titles or footer counters. + +## Customizing + +To restrict to a subset of players, add filters to the catalog: + +```hocon +catalog { + type: PLAYERS + filters { + permission: "myserver.online_visible" + notWorld: ["staff_world"] + } +} +``` + +To add more admin actions, extend the click block with `right`, `shift_right`, etc. - each pointing to its own command via the `command` action. + +To swap the catalog entirely (browse worlds, regions, online BungeeCord servers), change `catalog { type: ... }` to `WORLDS`, `BUNGEE_SERVERS`, or any other registered catalog type. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Make sure your account has the `abstractmenus.admin` permission. +4. Type `/ame_online` in-game. +5. Left-click a player head to teleport. Shift-left to kick. diff --git a/src/content/docs/en/examples/admin-tools/quick-mod.mdx b/src/content/docs/en/examples/admin-tools/quick-mod.mdx new file mode 100644 index 0000000..24c5b3a --- /dev/null +++ b/src/content/docs/en/examples/admin-tools/quick-mod.mdx @@ -0,0 +1,76 @@ +--- +title: Quick Mod +description: An admin-only quick-actions menu - gamemode shortcuts, broadcast, online-players browser, teleport to spawn. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A toolbox menu for admins. The menu itself is gated at the top level via `rules: ${rulesAdmin}` - non-admins running `/ame_mod` are blocked from opening at all (no need to gate every individual click). + + + +## What it teaches + +- **Menu-level rules**: top-level `rules: ...` blocks the open instead of just hiding items +- The `setGamemode` action with vanilla mode names +- The `broadcast` action for server-wide chat +- The `command` action with `player:` (run as the player) vs `console:` (run as console) +- Chaining hubs: this admin menu opens [Online Players](./online-players) for finer-grained moderation + +## Menu-level vs item-level rules + +Two ways to gate access: + +**Menu-level** (this example): + +```hocon +rules: ${rulesAdmin} +``` + +At the top of the file. If the rule fails when the activator fires, the menu doesn't open at all. + +**Item-level**: + +```hocon +{ + slot: 4 + ... + click { + rules { permission: "..." } + actions { ... } + } +} +``` + +Inside an item's click block. The menu opens for everyone, but specific items are gated. + +For an admin toolbox, menu-level is right - non-admins shouldn't see the layout. For a shop with VIP items, item-level is right - everyone should see what's available. + +## command action + +`command` runs server commands via the action handler: + +```hocon +command { + player: "spawn" # runs /spawn as the player +} +``` + +Or as console: + +```hocon +command { + console: "kick %ctg_player_name%" # runs /kick ... from console +} +``` + +`player:` form respects the player's permissions - they need to be allowed to use the command. `console:` form bypasses permissions (use carefully). For admin commands like /ban, prefer `console:` from a permission-gated menu to avoid double-checking the same auth twice. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Grant yourself `abstractmenus.admin` (the `rulesAdmin` shared template references this node). +4. Type `/ame_mod` in-game. diff --git a/src/content/docs/en/examples/builder.mdx b/src/content/docs/en/examples/builder.mdx new file mode 100644 index 0000000..fef39b9 --- /dev/null +++ b/src/content/docs/en/examples/builder.mdx @@ -0,0 +1,44 @@ +--- +title: Pack Builder +description: Pick which example menus to bundle into a custom zip. Mix-and-match across categories, get one download with everything you need. +--- + +import PackBuilder from '@components/PackBuilder.astro'; + +Download multiple example menus as one zip without opening each menu's page individually. Tick the menus you want, hit "Build pack", get a single archive with proper folder structure ready to drop into your server. + + + +## How to use + +1. Tick the menus you want. +2. Use the per-category checkbox to select all in that category at once. +3. "Select all" / "Select none" shortcuts at the top apply to everything. +4. Click "Build pack (.zip)". +5. Drop the zip into `plugins/AbstractMenus/menus/example/` on your server. +6. `/am reload`. +7. Run any of the bundled commands (each menu's name shows the command). + +The bundle uses the same folder structure as the wiki, so unzipping straight into `plugins/AbstractMenus/menus/example/` just works. + +## What's in the bundle + +For each selected menu: + +- `//menu.conf` - the menu configuration +- `//templates.conf` - if the menu has local templates + +Plus, once across the whole bundle (only if any selected menu uses it): + +- `_shared/templates.conf` - shared sound, deny, border, button, rule snippets + +And: + +- `INSTALL.txt` - install steps with the date the bundle was generated + +## Tips + +- **Need just snippets**: the snippets category is small minimal demos, one feature each. Good for learning. +- **Building a server out of the box**: pick a hub, a shop, kits, info pages, and one casino menu. About 10 menus covers a basic survival server. +- **Just trying things**: start with the snippets and the foundation menus (cake-shop, main-hub, basic-kit, rules) to get a feel. +- **Bundle is identical regardless of which language you pick**: structure is the same, only the in-game text differs. Use `/docs/ru/examples/builder/` for the Russian-string version. diff --git a/src/content/docs/en/examples/casino-and-games/daily-lottery.mdx b/src/content/docs/en/examples/casino-and-games/daily-lottery.mdx new file mode 100644 index 0000000..bbc0cd8 --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/daily-lottery.mdx @@ -0,0 +1,72 @@ +--- +title: Daily Lottery +description: A jackpot lottery using a global variable as the prize pool. Each ticket either wins everything or adds to the pot. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The most server-impacting menu in the casino category - a lottery with a real, growing jackpot. Each $100 ticket either wins the whole pot at 1-in-50 odds, or contributes $50 to it. The jackpot accumulates across all players, all sessions, until someone wins. + + + +## What it teaches + +- A global variable as a persistent prize accumulator +- The `incVar` action with an explicit amount (not just +1) +- Reading a global var inside another action: `giveMoney: "%var_:ame_lottery_jackpot:0%"` +- Combining `bulk` with inline `rules { chance: ... } actions { ... } denyActions { ... }` for branching behavior +- Server-wide announcement via `broadcast` + +## How the win/loss branch works + +Inside the click's `actions { }`, we use `bulk` to wrap a single conditional sub-action group: + +```hocon +bulk: [ + { + rules { chance: 2 } + actions { + # Win path + giveMoney: "%var_:ame_lottery_jackpot:0%" + setVar: "ame_lottery_jackpot::0" + ... + } + denyActions { + # Loss path + incVar { name: "ame_lottery_jackpot", amount: 50 } + ... + } + } +] +``` + +`rules { chance: 2 }` rolls a 2% probability. If true, `actions` runs (pay out + reset jackpot). If false, `denyActions` runs (add $50 to jackpot). + +The `bulk` wrapper is here so this whole conditional is one entry in a list - lets us add more conditional outcomes later (10% chance for half-jackpot, etc.) by appending to the bulk list. + +## Reading vars in action arguments + +`giveMoney: "%var_:ame_lottery_jackpot:0%"` - the placeholder gets resolved to the current jackpot value (e.g., `5000`), and `giveMoney` receives that number. Combined with `setVar: "ame_lottery_jackpot::0"` immediately after, this is the "pay out then reset" pattern. + +Order matters: payout must happen before the reset, otherwise the player gets $0. + +## Caveats + +This is a self-contained demo. Real lottery systems usually want: + +- **Persistence**: ensure the jackpot var survives server restarts (covered by AbstractMenus's variable storage if `syncVariables: true` in config) +- **Per-day limits**: temporal var per player (`setVarp ::1d`) to limit ticket purchases +- **Admin reset/audit**: this menu has a basic admin reset; real ops want logged transactions +- **Anti-cheat**: rate limiting + click debouncing already handled by the plugin's per-menu cooldowns + +For your production implementation, layer those on top of the pattern shown here. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 5000`. +4. Type `/ame_lottery` in-game. +5. Buy 20-30 tickets. The jackpot grows. Eventually one of them wins. diff --git a/src/content/docs/en/examples/casino-and-games/enchanter.mdx b/src/content/docs/en/examples/casino-and-games/enchanter.mdx new file mode 100644 index 0000000..bb8130d --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/enchanter.mdx @@ -0,0 +1,78 @@ +--- +title: Random Enchanter +description: A drag-and-drop enchant lottery. Drop a tool in, click "Enchant!", get a random enchantment back. Demonstrates draggable slots, placeItem, placedItem rules, and randActions. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The most feature-dense menu in the pack. Combines drag-and-drop slots (the player physically places an item in the menu), the `placedItem` rule (check what's in a slot), `placeItem` action (write into a slot), `removePlaced` (clear a slot), `setButton` (replace a static item), and `randActions` (pick one outcome at random). All of it together gives the player a "throw an item in, gamble for an enchantment" experience. + +## What it teaches + +- The `draggable:` field with a matrix string defining which slots accept dropped items +- The `placedItem` rule for checking the contents of a draggable slot +- Inverted rules with the `-` prefix: `-placedItem { ... }` means "NOT placed" +- The `placeItem` action for writing items into draggable slots +- `removePlaced` to clear a draggable slot +- `setButton` to overwrite a regular menu item dynamically +- The `%changed_item_*%` placeholder family that captures what the player dragged +- Composing `randActions` with `placeItem` for randomized outcomes + + + +## How drag-and-drop works + +The `draggable:` field on the menu root takes a matrix of strings, one per row, where `x` marks a slot players can drop items into. In this example slot 11 (input) and slot 15 (result) are draggable. + +```hocon +draggable: [ + "---------", + "--x---x--", + "---------" +] +``` + +When a slot is draggable, the player can pick items up from their hotbar/inventory and drop them into that slot. The plugin tracks what's there separately from menu items - drag-and-drop slots are a different overlay on top of the menu grid. + +Inside actions, you can: + +- **`placedItem` rule** - check what's in a draggable slot. `placedItem { slot: 11, material: AIR }` evaluates true when slot 11 is empty. +- **`-placedItem`** - inverted; same check, opposite result. +- **`placeItem` action** - write an item into a draggable slot. +- **`removePlaced` action** - clear a draggable slot. + +## How the random enchant works + +The enchant button's click runs three steps: + +1. **Rule check**: `-placedItem { slot: 11, material: AIR }` - "slot 11 is NOT empty". If the player hasn't dropped anything, fall through to denyActions. +2. **`randActions`**: pick one entry at random from a list. Each entry calls `placeItem` to put a copy of the player's item (preserved via `%changed_item_serialized%`) into slot 15, with one of four enchant sets applied. +3. **`removePlaced: 11`**: clear the input slot. The original item is gone, the enchanted copy is in slot 15. + +The deny path (when input is empty) plays a sound and uses `setButton: ${resultStub}` to reset the result-slot placeholder back to the gray pane. + +## Customizing + +To add another enchant outcome, append to the `randActions` list: + +```hocon +{ placeItem: ${placeItemBase} { enchantments { mending: 1 } } } +``` + +Each outcome has equal weight by default. For weighted random, repeat outcomes (an item appearing twice in the list is twice as likely to be picked). + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_enchanter` in-game. +4. Drag a sword/pickaxe into the left slot. +5. Click "Enchant!". The result appears in the right slot with a random enchant. diff --git a/src/content/docs/en/examples/casino-and-games/index.mdx b/src/content/docs/en/examples/casino-and-games/index.mdx new file mode 100644 index 0000000..9d2cc86 --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/index.mdx @@ -0,0 +1,18 @@ +--- +title: Casino & Games +description: Animated roulettes, slot machines, lucky chests, and a daily lottery using global variables as jackpot. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Animated roulettes, slot machines, lucky chests, and a daily lottery using global variables as jackpot. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/casino-and-games/lucky-chest.mdx b/src/content/docs/en/examples/casino-and-games/lucky-chest.mdx new file mode 100644 index 0000000..b9a1a49 --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/lucky-chest.mdx @@ -0,0 +1,55 @@ +--- +title: Lucky Chest +description: Pay $50, open a chest, get one of 4 reward tiers. Demonstrates weighted random outcomes via repeated randActions entries. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A pay-to-open lottery chest. Each click costs $50 and rolls one of four prize tiers (common / uncommon / rare / mythic) with weighted probability. Built with `randActions` and the "repeat entries to add weight" pattern. + + + +## What it teaches + +- Weighted random outcomes via repeated `randActions` entries +- Combining a money rule, a takeMoney action, and randActions in one click +- A tiered loot table without writing custom Java code + +## Weighted random by repetition + +`randActions` picks one entry uniformly at random. To make some outcomes more likely than others, just include them multiple times. With 12 entries: + +- **6 entries** of the common reward = 6/12 = 50% (the example targets ~60%) +- **3 entries** of the uncommon = 3/12 = 25% +- **1 entry** of the rare = 1/12 = ~8% +- **2 entries** of the mythic = 2/12 = ~17% (target was 2%, see note) + +The list above is verbose - 12 lines for 4 prize types. It's still much more readable than a JS expression with weighted-random math, and it's bulletproof correct (no chance of typoing the weight). + +For finer-grained probabilities (e.g., 1.5%), use a list with 200+ entries. Or define the prize as a separate template and reference it many times to keep the list compact: + +```hocon +prizeCommon { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ... } +prizeRare { itemAdd { material: ENCHANTED_GOLDEN_APPLE }, sound: ... } + +randActions: [ + ${prizeCommon} + ${prizeCommon} + ${prizeCommon} + ${prizeRare} +] +``` + +## Note on accurate weights + +The probabilities listed in the menu lore (60/30/8/2) don't match the actual list weights exactly - the demo is approximate to keep the list short. To get exact 60/30/8/2 you'd need 50 entries (30/15/4/1). Adjust to your taste. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 1000`. +4. Type `/ame_chest` in-game. +5. Click "Open Chest". Repeat to see different tiers. diff --git a/src/content/docs/en/examples/casino-and-games/roulette.mdx b/src/content/docs/en/examples/casino-and-games/roulette.mdx new file mode 100644 index 0000000..9a90c71 --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/roulette.mdx @@ -0,0 +1,88 @@ +--- +title: Roulette +description: An 8-frame animated roulette that slows down toward the end, plus a random prize awarded via randActions when the animation finishes. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Open the menu and an item icon flips through Diamond → Emerald → Gold → Diamond..., progressively slowing down. When it stops, the player gets a random reward (or nothing). Demonstrates the AnimatedMenu format with `frames`, the slowdown effect via increasing `delay`, and `onAnimEnd` for "what happens after the animation". + +## What it teaches + +- The `frames` block as an alternative to `items` for animated menus +- Per-frame `delay:` (in ticks) and `clear: false` to preserve static items +- The static `items:` block working alongside `frames` +- `onAnimEnd` hook for triggering actions when the animation completes +- `randActions` for picking one of N outcomes at random +- Designing the animation pacing - rapid frames at start, slow at the end + + + +## How AnimatedMenu works + +When a menu file has a `frames:` block at the root, the plugin treats it as an animated menu. `frames` replaces the usual single-pass `items` rendering with a sequence of frames played in order. + +Each frame has: + +- **`delay:`** ticks before this frame plays (default 20 = 1 second) +- **`clear:`** whether to clear the inventory before this frame's items render (default `true`) +- **`items:`** the items to add for this frame +- Optional `rules`, `onStart`, `onEnd` + +A static `items:` block at the root works alongside frames. With `clear: false` on frames, those static items persist - exactly what we want for borders and the close button. + +## The slowdown trick + +Roulettes feel right when they slow down at the end. We do this with progressively bigger `delay:` values: + +```hocon +frames: [ + { delay: 2, ... } # 0.1s - very fast + { delay: 2, ... } + { delay: 2, ... } + { delay: 2, ... } + { delay: 4, ... } # starts slowing + { delay: 6, ... } + { delay: 10, ... } + { delay: 20, ... } # 1s - dramatic pause on final frame +] +``` + +Total: roughly 2.4 seconds. The final NETHER_STAR frame holds for a full second while `onAnimEnd` fires the prize. + +## Awarding the prize + +`onAnimEnd` runs after the last frame finishes. It contains a `randActions` block that picks one of four outcomes at random: + +```hocon +onAnimEnd { + randActions: [ + { sound: ${successSound}, itemAdd { material: DIAMOND }, message: "..." } + { sound: ${successSound}, itemAdd { material: EMERALD, count: 2 }, message: "..." } + ... + { sound: ${failSound}, message: "&7Better luck next time." } + ] +} +``` + +Each entry has its own actions block. One is picked uniformly at random. The "no win" outcome is just a sound + message with no `itemAdd`. + +## Customizing + +To weight outcomes differently, repeat them. An outcome appearing twice in the list is twice as likely. To change the spin duration, edit the `delay:` values - the total spin time is the sum of all delays. + +To change what items appear during the spin, swap the `material:` in each frame's items. They're decorative - the actual prize comes from `onAnimEnd`, not from "where the spin lands". + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_roulette` in-game. Watch the spin, see what you win. diff --git a/src/content/docs/en/examples/casino-and-games/slot-machine.mdx b/src/content/docs/en/examples/casino-and-games/slot-machine.mdx new file mode 100644 index 0000000..f340539 --- /dev/null +++ b/src/content/docs/en/examples/casino-and-games/slot-machine.mdx @@ -0,0 +1,39 @@ +--- +title: Slot Machine +description: Three reels spinning together. Animation-driven with weighted random outcome distribution. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A slot machine with three reels and four outcome tiers (miss / cherries / bells / jackpot). Closer to a real slot machine UX than [Roulette](./roulette) - more reels, more visual texture, more dramatic slowdown. Same `frames` + `onAnimEnd` + `randActions` building blocks. + + + +## What it teaches + +- Multi-slot animation: each frame updates 3 reels at once +- Weighted prize tables (3/1/1/1 in the randActions list ≈ 60/20/20/something) +- Pairing animation visuals (the FINAL frame shows triple-stars) with the actual prize roll (independent in randActions) + +## Reels in lockstep + +Each frame's items list contains three items - one per reel. They all update on the same frame transition, so visually all three reels rotate together rather than independently. + +True independent reels would need three separate animated menus stacked, or extension code that drives each reel on its own clock. For a demo, lockstep reels are fine - the player gets the slot-machine feel without the engineering overhead. + +## Decoupling animation from outcome + +The visual frames show various symbol combinations. The actual prize is decided by `randActions` in `onAnimEnd`, completely independently of the last frame's symbols. + +This is intentional. If you tied the prize to the visual outcome (e.g., "you saw three cherries, you get cherries prize"), you'd need to control which symbol the animation lands on, which requires per-roll dynamic frames. Far more complex. + +The decoupled approach is what real slot machines actually do. The visuals are theater. The math is in the prize roll. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 500`. +4. Type `/ame_slots` in-game. Watch the spin. See the prize message. diff --git a/src/content/docs/en/examples/cosmetics/color-skins.mdx b/src/content/docs/en/examples/cosmetics/color-skins.mdx new file mode 100644 index 0000000..3c16bfa --- /dev/null +++ b/src/content/docs/en/examples/cosmetics/color-skins.mdx @@ -0,0 +1,65 @@ +--- +title: Color Skins +description: A skin chooser menu using setSkin via SkinsRestorer. Three preset skins plus a reset to default. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The `setSkin` action swaps the player's skin through SkinsRestorer. This example shows three preset skins plus a reset button. The `closeMenu` call right before `setSkin` is mandatory - changing the skin respawns the player, and the open inventory would crash the client. + +## What it teaches + +- The `setSkin` action with `texture` + `signature` parameters +- The `resetSkin: true` shortcut to restore default skin +- Why `closeMenu` must run before `setSkin` (respawn + open inventory crashes the client) +- Using `PLAYER_HEAD` with a `texture:` field to render a skin preview as the menu item + + + +## Important: order of actions + +Inside the `actions { ... }` block, keys are executed in the order they appear in the file. The plugin docs explicitly require `closeMenu` before `setSkin`: + +```text +:::caution +Before this action, you need to set the closeMenu action, because when the +skin is changed, player respawning. If the menu is opened, it may cause a +critical error for the client. +::: +``` + +In each item's click block above, `closeMenu: 1` comes first, then `setSkin`. Don't reorder. + +## Getting real texture and signature + +The placeholders in this example (`REPLACE_WITH_TEXTURE_BASE64_GOLD` etc.) won't work as-is. To get real values: + +1. Go to [MineSkin.org](https://mineskin.org). +2. Upload an image or pick a preset. +3. Copy the **Texture Data** field into the `texture:` parameter. +4. Copy the **Texture Signature** field into the `signature:` parameter. + +The two fields are long base64 strings. The `texture` on the displayed `PLAYER_HEAD` (the preview) is the shorter texture-hash form (just the hash from the `http://textures.minecraft.net/texture/` URL). + +## Customizing + +To add more skin choices, copy any item block, change the `slot:`, `name:`, the preview `texture:`, and the `setSkin` payload. The shared template's `${successSound}` keeps the audio feedback consistent across choices. + +To skip the preview (use a non-head item like a banner), drop the `texture:` field and change `material:` to `RED_BANNER` or whatever fits the visual theme. + +## Try it + +After installing the example pack: + +1. Install [SkinsRestorer](https://www.spigotmc.org/resources/2124/). +2. Replace the `REPLACE_WITH_*` placeholders with real texture/signature values. +3. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +4. `/am reload`. +5. Type `/ame_skins` in-game. +6. Click a skin. Reconnect to see the change. diff --git a/src/content/docs/en/examples/cosmetics/index.mdx b/src/content/docs/en/examples/cosmetics/index.mdx new file mode 100644 index 0000000..48dd6b3 --- /dev/null +++ b/src/content/docs/en/examples/cosmetics/index.mdx @@ -0,0 +1,18 @@ +--- +title: Cosmetics +description: Tag, prefix, and skin selectors using lpMetaSet, addGroup/removeGroup, and template substitution for variants. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Tag, prefix, and skin selectors using lpMetaSet, addGroup/removeGroup, and template substitution for variants. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/cosmetics/prefixes.mdx b/src/content/docs/en/examples/cosmetics/prefixes.mdx new file mode 100644 index 0000000..009ca72 --- /dev/null +++ b/src/content/docs/en/examples/cosmetics/prefixes.mdx @@ -0,0 +1,68 @@ +--- +title: Prefixes +description: A prefix selector that puts the player in a LuckPerms group. Mutual-exclusion via removeGroup-then-addGroup in a bulk action. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A different approach from [Tags](./tags). Instead of writing meta values, this menu manages LuckPerms group membership. Each prefix corresponds to one group; selecting a prefix removes the player from the other prefix groups and adds them to the chosen one. Mutual exclusion built in. + + + +## What it teaches + +- The `addGroup` / `removeGroup` actions for LuckPerms group membership +- Using `bulk` to run multiple removeGroups + addGroup in one click +- Permission-gated upgrades to higher-tier prefixes + +## Why bulk + +Each prefix selection runs three actions in sequence: + +```hocon +bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_donor" } + { addGroup: "ame_prefix_admin" } +] +``` + +We can't write `removeGroup: ...` twice at the top level of an `actions {}` block - HOCON object keys must be unique. `bulk` wraps the action sequence, letting each entry be its own action group. + +This pattern (remove-all-others, add-this-one) ensures the player is in exactly one prefix group at a time. Without the removes, players accumulate group memberships and end up with multiple stacked prefixes. + +## Tags vs Prefixes pattern + +Two ways to manage chat prefix: + +- **Tags** (lpMetaSet) - flexible, fine-grained, no group hierarchy. Best for purely cosmetic symbols. +- **Prefixes** (addGroup) - integrates with LuckPerms groups, inherits perms from the group, plays nice with chat plugins that read `%luckperms_prefix%`. Best when the prefix should also imply other permissions. + +Most servers want the latter for the actual chat name color, and the former for an additional decorative tag. + +## Setup + +Create the LuckPerms groups before this menu works: + +```text +/lp creategroup ame_prefix_member +/lp group ame_prefix_member meta addprefix 50 "&a[Member] " + +/lp creategroup ame_prefix_donor +/lp group ame_prefix_donor meta addprefix 100 "&6[Donor] " + +/lp creategroup ame_prefix_admin +/lp group ame_prefix_admin meta addprefix 1000 "&c[Admin] " +``` + +The numeric weight (`50`, `100`, `1000`) controls priority when a player has multiple prefixes - higher wins. Since this menu enforces exactly one prefix per player, weight doesn't matter much, but it's good practice to set them. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. Create the three groups above. +3. `/am reload`. +4. Type `/ame_prefixes` in-game. +5. Click any prefix. Verify with `/lp user info` (you'll see one of the prefix groups in your group list). diff --git a/src/content/docs/en/examples/cosmetics/tags.mdx b/src/content/docs/en/examples/cosmetics/tags.mdx new file mode 100644 index 0000000..aa09faa --- /dev/null +++ b/src/content/docs/en/examples/cosmetics/tags.mdx @@ -0,0 +1,66 @@ +--- +title: Tags +description: A tag selector that writes a custom symbol to the player's LuckPerms meta. Free tags for everyone, a VIP-gated crown tag for premium players. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Most servers display a chat tag (a small symbol next to the player's name) by reading from a `LuckPerms` meta key. This menu lets players pick which tag they want by writing the chosen symbol to that meta key. Pair with a chat-format plugin that reads `%luckperms_meta_ame_tag%` (or whatever your chat formatter expects) and the tag shows up in chat. + + + +## What it teaches + +- The `lpMetaSet` action with a `metaList` of key/value pairs +- The `lpMetaRemove` action for clearing a tag +- Mixing free tags with permission-gated premium tags in one menu +- The dual-action pattern: `actions { ... } denyActions: ${denyNoPerm}` for the VIP-only tag + +## How LuckPerms meta works + +`lpMetaSet` writes one or more meta values onto the player's LuckPerms profile: + +```hocon +lpMetaSet { + metaList: [ + { key: "ame_tag", value: "&c♥" } + ] +} +``` + +The meta key (`ame_tag` here) is a string of your choosing - pick something server-specific. The value is the actual content (a colored symbol in this example). Multiple meta keys can be set in one action: + +```hocon +metaList: [ + { key: "prefix", value: "&7[Member]" }, + { key: "ame_tag", value: "&c♥" }, + { key: "show_role", value: "true" } +] +``` + +`lpMetaRemove` takes a string list of keys to delete: + +```hocon +lpMetaRemove: ["ame_tag"] +``` + +These actions only work when LuckPerms is the active permissions provider. If a different provider is active, the action logs a warning and does nothing - so the menu won't crash, just won't apply the tag. + +## Showing the tag in chat + +Setting the meta is half the work. The chat formatter has to read it. Most chat plugins (DeluxeChat, ChatFormatter, etc.) accept PlaceholderAPI `%luckperms_meta_%` placeholders in their format string: + +```yaml +chatformat: "&8[%luckperms_meta_ame_tag%&8] &f%player_name%: %message%" +``` + +Once that's in your chat plugin's config, restart, and the tag appears next to the player's name. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_tags` in-game. +4. Click any free tag - the meta is set. Verify with `/lp user meta info` (you'll see `ame_tag` in the list). diff --git a/src/content/docs/en/examples/donate/index.mdx b/src/content/docs/en/examples/donate/index.mdx new file mode 100644 index 0000000..bf6f698 --- /dev/null +++ b/src/content/docs/en/examples/donate/index.mdx @@ -0,0 +1,18 @@ +--- +title: Donate +description: Tiered rank stores and item shops with HAS/NO_PERM dual-variant items. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Tiered rank stores and item shops with HAS/NO_PERM dual-variant items. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/donate/item-store.mdx b/src/content/docs/en/examples/donate/item-store.mdx new file mode 100644 index 0000000..b926289 --- /dev/null +++ b/src/content/docs/en/examples/donate/item-store.mdx @@ -0,0 +1,59 @@ +--- +title: Item Store +description: A donor item store. Buy enchanted items, elytra, decorative heads with donor tokens (or any economy currency). +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The companion to [Tiered Donate Ranks](./tiered-ranks). Where the rank store sells permissions, this one sells items - things players can use directly. Same money/takeMoney pattern as a regular shop, but priced higher because it's intended to be paid for with donor currency rather than gameplay-earned coins. + + + +## What it teaches + +- Custom-named, pre-enchanted items via `itemAdd { name: ..., enchantments { ... } }` +- The `flags: HIDE_ATTRIBUTES` flag for tools/swords (otherwise vanilla shows damage range below name) +- Pricing items in the donor currency band + +## Picking an economy provider + +By default, `takeMoney: 1000` uses whatever provider is configured at `config.conf providers.economy`. For a donor-token system you typically want this to route through a separate provider (like the [PlayerPoints addon](https://github.com/AbstractMenus/PlayerPointsAddon)) instead of the regular Vault economy. + +Two options: + +**1. Server-wide donor currency:** edit `config.conf` to make donor tokens the default economy. All `takeMoney` calls use it. Simplest if your whole server runs on tokens. + +**2. Per-action provider override:** keep Vault as default, but specify the donor provider on each action in this menu: + +```hocon +takeMoney: { amount: 1000, provider: "playerpoints" } +``` + +This lets the regular shop continue using Vault while the donor store charges tokens. + +## Custom item shapes + +The Mythic Sword item demonstrates building a non-vanilla item via `itemAdd`: + +```hocon +itemAdd { + material: NETHERITE_SWORD + name: "&dMythic Sword" + enchantments { + sharpness: 5 + unbreaking: 3 + mending: 1 + } +} +``` + +Any field that works on a menu display item works here too - lore, attribute modifiers, custom NBT, durability, model. See the [item format docs](/docs/en/general/item-format/) for the full list. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 2000`. +4. Type `/ame_store` in-game. diff --git a/src/content/docs/en/examples/donate/tiered-ranks.mdx b/src/content/docs/en/examples/donate/tiered-ranks.mdx new file mode 100644 index 0000000..ec79ae6 --- /dev/null +++ b/src/content/docs/en/examples/donate/tiered-ranks.mdx @@ -0,0 +1,46 @@ +--- +title: Tiered Donate Ranks +description: A 4-tier donate store. Each tier shows differently to players who already own it vs. don't. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The standard donate store layout: rank tiles in increasing order, each one shows "Owned" if the player already has the permission, or the price + perks list if they don't. Built on the dual-item display pattern with one variant per rank state. + + + +## What it teaches + +- Scaling the dual-item pattern across many tiers +- Command aliases via `command: [..., ...]` (so both `/ame_donate` and `/ame_support` open it) +- Per-rank perk listing in lore +- Where to wire the actual purchase flow (this example just sends a chat message) + +## How the dual variants stack + +Each rank slot has two items declared. The HAS_RANK variant has `rules { permission: "myserver.rank.knight" }` and shows "Owned" + "Consider upgrading below." The NO_RANK variant has no rules and shows the perks + price. + +Same rendering rule as [Rank-Gated Shop](../shops/rank-gated-shop): walk items in declaration order; the first item at each slot whose rules pass renders. HAS_RANK is declared first, so when the player has the permission, that variant wins. + +## Wiring the purchase flow + +Real donate stores integrate with a payment processor. Common patterns: + +- **External URL**: send `&aVisit https://example.org/donate to purchase` in the click action. Player buys on the website, gets the rank via API. +- **In-game currency**: gate with `money` rule and `takeMoney` action like a regular shop, then grant rank with `addGroup: "knight"`. +- **Player Points / token system**: same as money but via a different provider. Pin the provider explicitly: `takeMoney: { amount: 5, provider: "playerpoints" }`. + +This example uses the URL pattern because it's the most generic. Swap the click block to fit your store. + +## Adding a fifth tier + +Copy any pair (HAS_RANK + NO_RANK) for a tier, change the slot to a free position (slots 27 or 29 in row 4 fit well), update the permission name, perks, and price. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_donate` or `/ame_support` in-game. +4. Grant yourself a permission (e.g., `lp user permission set myserver.rank.knight true`) to see the HAS_RANK variant render. diff --git a/src/content/docs/en/examples/hub-and-nav/breadcrumb-chain.mdx b/src/content/docs/en/examples/hub-and-nav/breadcrumb-chain.mdx new file mode 100644 index 0000000..4671bc6 --- /dev/null +++ b/src/content/docs/en/examples/hub-and-nav/breadcrumb-chain.mdx @@ -0,0 +1,38 @@ +--- +title: Breadcrumb Chain +description: Three nested menus with manual back-navigation. The simplest pattern for menu trees. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Three menus chained together with `openMenu`, each level having a back button to the previous one. Demonstrates how to build menu trees several levels deep without anything fancier than the basic openMenu action. + + + +## What it teaches + +- Building deeper menu trees with manual openMenu chains +- Per-level back buttons hard-coded to the parent + +## Manual vs context-aware back + +Each level's back button hard-codes the parent menu name: + +```hocon +{ slot: 0, name: "&eBack to Level 2", click { openMenu: "ame_chain_l2" } } +``` + +Simple, predictable, works for tree structures where every menu has exactly one parent. Trade-off: if `ame_chain_l2` is opened from multiple parents (say, both Level 1 AND a sub-hub), the back button always returns to Level 1, ignoring where the player actually came from. + +For DAG-shaped menu graphs where a menu can be reached from multiple places and back should respect the actual arrival path, use `openMenuCtx` instead. It propagates context through the menu chain. See the [activator/menu input docs](/docs/en/advanced/input/) for `openMenuCtx` mechanics. + +For 95% of menus a simple tree is fine - hard-coded back buttons keep things predictable. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_chain` in-game. +4. Click "Go deeper" twice. Click back twice. diff --git a/src/content/docs/en/examples/hub-and-nav/index.mdx b/src/content/docs/en/examples/hub-and-nav/index.mdx new file mode 100644 index 0000000..aa63d9b --- /dev/null +++ b/src/content/docs/en/examples/hub-and-nav/index.mdx @@ -0,0 +1,18 @@ +--- +title: Hub & Navigation +description: Main hubs and sub-menus showing openMenu, back-button patterns, and breadcrumb chains via openMenuCtx. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Main hubs and sub-menus showing openMenu, back-button patterns, and breadcrumb chains via openMenuCtx. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/hub-and-nav/main-hub.mdx b/src/content/docs/en/examples/hub-and-nav/main-hub.mdx new file mode 100644 index 0000000..61bea62 --- /dev/null +++ b/src/content/docs/en/examples/hub-and-nav/main-hub.mdx @@ -0,0 +1,46 @@ +--- +title: Main Hub +description: A launcher menu that opens any of the example pack menus from one place. The same pattern fits any "select a category" use case. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A hub menu is just a list of buttons that each open another menu. Useful as a server's main `/menu` command or as a way to organize a large collection of sub-features. This example links to the other menus in the pack so you can use it as a launcher while exploring. + +## What it teaches + +- The `openMenu` action - the only thing the buttons do +- A simple 3-row layout with borders, header item, and tile row +- A reusable pattern for any "select a category" UI + + + +## How it works + +Every tile is the same shape: an item with `material:`, `name:`, `lore:`, and a `click { openMenu: "ame_" }`. There are no rules, no actions other than navigation, no money checks. The hub's job is just to dispatch. + +The header item at slot 4 is purely decorative - same as in [Cake Shop](../shops/cake-shop). No click handler. + +## Customizing + +To add a new tile: + +1. Pick a free slot (10-16 in this layout work well). +2. Copy any tile block. +3. Change `material:`, `name:`, `lore:`, and the `openMenu:` target. + +The hub has no dependency on which menus actually exist. If a tile points at a non-existent menu, clicking it just shows a "menu not found" warning - useful for staging future menus before they're built. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_hub` in-game. diff --git a/src/content/docs/en/examples/hub-and-nav/sub-hub.mdx b/src/content/docs/en/examples/hub-and-nav/sub-hub.mdx new file mode 100644 index 0000000..10b0208 --- /dev/null +++ b/src/content/docs/en/examples/hub-and-nav/sub-hub.mdx @@ -0,0 +1,45 @@ +--- +title: Sub-Hub +description: A secondary hub with a back-button pointing at the main hub. The standard "more options" menu. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +When the main hub gets crowded, split overflow features onto a secondary hub. This example follows the most common pattern: same layout as the main hub, but with a back button pointing at the parent. + + + +## What it teaches + +- The back-button pattern via `openMenu` to the parent's name +- Distinguishing "back" (just go up the tree) from "close" (exit the menu UI) +- Allowing the sub-hub to be opened directly via its own command in addition to chained navigation + +## Two ways to "go back" + +Approach 1 (used here): hard-code the parent name. + +```hocon +{ + slot: 18 + material: ARROW + name: "&eBack to Main Hub" + click { openMenu: "ame_hub" } +} +``` + +Cheap, simple, works. Limitation: if multiple menus chain into this sub-hub, the back button always lands on `ame_hub`, even if the player came from somewhere else. + +Approach 2 (see [Breadcrumb Chain](./breadcrumb-chain)): use `openMenuCtx` and let the activator context propagate the "previous menu" through the chain. The back button then dynamically returns to wherever the player came from. + +For 95% of menus, hard-coding parent is fine. Reserve `openMenuCtx` for genuinely deep menu trees. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_hub` to start at the main hub, or `/ame_subhub` to land here directly. +4. Click any tile to open its target menu. +5. Click "Back to Main Hub" to return. diff --git a/src/content/docs/en/examples/index.mdx b/src/content/docs/en/examples/index.mdx new file mode 100644 index 0000000..599ead1 --- /dev/null +++ b/src/content/docs/en/examples/index.mdx @@ -0,0 +1,38 @@ +--- +title: Examples +description: A catalog of ready-to-use AbstractMenus menu examples - copy, drop in, customize. +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; + +The Examples Pack is a catalog of ~46 ready-to-use menus organized by use-case. Every example is downloadable as a single `.conf` file or as a bundle (`.zip`) you drop into your server. + +## How to use + +Each example shows the full `menu.conf` with syntax highlighting, plus badges describing what plugins it needs and what features it teaches. Below the code: a **Download** button for the single file, and a **Bundle** button that ships the menu plus any shared templates it needs, plus an `INSTALL.txt` with install steps. + +The Bundle keeps the same folder layout as the wiki, so unzipping it into `plugins/AbstractMenus/menus/example/` gives you a working setup. Every example uses a command activator with the prefix `ame_` (e.g. `/ame_cake_shop`) to avoid colliding with your existing commands. + +## Categories + + + + + + + + + + + + + + + +## Shared templates + +All examples optionally include a small `_shared/templates.conf` (~80 lines) with sound presets, deny actions, border items, navigation buttons, rule shortcuts, and a common header. The bundle automatically includes it when needed. + +## Compatibility + +All examples target **AbstractMenus 2.0.0+** and **Minecraft 1.21+**. Per-example badges call out external plugin dependencies (Vault, LuckPerms, WorldGuard, Citizens, etc.). diff --git a/src/content/docs/en/examples/info-pages/faq.mdx b/src/content/docs/en/examples/info-pages/faq.mdx new file mode 100644 index 0000000..a2dca6b --- /dev/null +++ b/src/content/docs/en/examples/info-pages/faq.mdx @@ -0,0 +1,49 @@ +--- +title: FAQ +description: A list of questions, click each to open a sub-menu with the answer. Multi-menu file with a back-button on each answer page. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A FAQ is essentially a navigation tree: question list at the root, one sub-menu per answer. This example uses a `menus { ... }` multi-menu file to keep all four screens in one config, with `openMenu` calls to wire them together. The `${buttonBack}` shared button gets a per-page override to point back at the FAQ index. + + + +## What it teaches + +- Using `menus { ... }` to declare a hub + multiple answer screens in one file +- Overriding shared template fields per use - `${buttonBack} { slot: 18, click { openMenu: "..." } }` +- The "answer page" pattern: large lore item + back button, no other interactivity +- One question pointing to a different existing menu (the rules question opens the [Server Rules](./rules) menu) + +## Why multi-menu files for FAQs + +A 10-question FAQ as 10 separate files is messy - 10 file paths, 10 `include` lines if shared templates. Bundling them in one `menus { ... }` block keeps related screens together and makes editing the FAQ a one-file operation. + +The naming convention matters: each menu gets a unique server-wide name. Pick `ame_faq`, `ame_faq_a1`, `ame_faq_a2` etc. so they don't collide with other example menus or the user's own configs. + +## Overriding `${buttonBack}` + +The shared `buttonBack` template defines an arrow item with `closeMenu: true`. For the FAQ, we want it to navigate back to the index instead. Override the click block per use: + +```hocon +${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } +``` + +The `${buttonBack}` brings in the material, name, lore. The override replaces the click block. Same pattern for any shared template you need to specialize per-callsite. + +## Adding a question + +Two edits per new question: + +1. In `ame_faq` items list, add a paper tile pointing at the new answer menu name. +2. Add a new `ame_faq_aN` block at the bottom of the `menus { ... }` wrapper with the answer content. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_faq` in-game. +4. Click any question. Read the answer. Click the back arrow to return. diff --git a/src/content/docs/en/examples/info-pages/index.mdx b/src/content/docs/en/examples/info-pages/index.mdx new file mode 100644 index 0000000..1ac5b80 --- /dev/null +++ b/src/content/docs/en/examples/info-pages/index.mdx @@ -0,0 +1,18 @@ +--- +title: Info Pages +description: Read-only menus showing server rules, external links, and paginated FAQs. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Read-only menus showing server rules, external links, and paginated FAQs. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/info-pages/rules.mdx b/src/content/docs/en/examples/info-pages/rules.mdx new file mode 100644 index 0000000..e3fbb31 --- /dev/null +++ b/src/content/docs/en/examples/info-pages/rules.mdx @@ -0,0 +1,51 @@ +--- +title: Server Rules +description: A read-only menu showing server rules. Players open it, scroll, close it. No interaction logic at all. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The simplest possible menu: just text. Four rules each rendered as a paper item with a multi-line lore explanation. No click handlers, no rules, no actions. The only interactive element is the close button. + +## What it teaches + +- Items without `click {}` are passive - players can mouse over them but clicks do nothing +- Multi-line `lore: [ ... ]` for rich text on a single item +- A 5-row menu with `size: 5` and how slot numbers map to rows +- Mixing decorative borders with content + + + +## How it works + +`size: 5` gives 45 slots (rows 0..4). The rules sit in the middle row (row 2, slots 18-26) at every-other-slot positions for breathing room. + +There are no `click` blocks on the rule items because there is nothing to click for. Players just hover to read the lore. + +The close button is the only interactive item, sourced from the shared `${buttonClose}` template. + +## Customizing + +To add a fifth rule, drop another paper item with a free slot in row 2 or row 3. Slot numbers go left-to-right within each row: + +- Row 0: 0-8 (top border in this example) +- Row 1: 9-17 +- Row 2: 18-26 (rules) +- Row 3: 27-35 +- Row 4: 36-44 (bottom border) + +To reword existing rules, just edit the `name:` and `lore:` fields. No code change required. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_rules` in-game. diff --git a/src/content/docs/en/examples/info-pages/server-links.mdx b/src/content/docs/en/examples/info-pages/server-links.mdx new file mode 100644 index 0000000..d528dc5 --- /dev/null +++ b/src/content/docs/en/examples/info-pages/server-links.mdx @@ -0,0 +1,51 @@ +--- +title: Server Links +description: Click any tile to open a written book with a clickable external URL. Useful for Discord invites, website links, GitHub, donate page, etc. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Minecraft chat lets players click URLs in chat or written books. The simplest way to expose external links from a menu is to open a single-page book per link - the book renders the URL as clickable text right in the player's UI. + + + +## Why books and not chat messages + +A chat message with a URL is fine but easy to scroll past. A written book opens in the player's view, looks more deliberate, and the URL is rendered as a big clickable button. For a "links" menu, books are the better UX. + +## openBook format + +```hocon +openBook { + author: "MyTestServer" + title: "&5Discord" + pages: [ + "Click the link below to open our invite:\n\nhttps://discord.gg/example" + ] +} +``` + +`pages` is a list of strings; each string is one page. Newlines inside a string break to new lines on that page. Plain URLs become clickable automatically when Minecraft renders the book. + +For a multi-page resource catalog, list more strings: + +```hocon +pages: [ + "Page 1: Discord invite\nhttps://discord.gg/...", + "Page 2: Website\nhttps://example.org", + "Page 3: GitHub\nhttps://github.com/..." +] +``` + +## Closing the menu + +Each click block calls `closeMenu: true` after opening the book. Otherwise the menu would stay open behind the book - confusing. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_links` in-game. +4. Click any tile. A book opens with a clickable URL inside. diff --git a/src/content/docs/en/examples/kits-and-rewards/basic-kit.mdx b/src/content/docs/en/examples/kits-and-rewards/basic-kit.mdx new file mode 100644 index 0000000..ff8c714 --- /dev/null +++ b/src/content/docs/en/examples/kits-and-rewards/basic-kit.mdx @@ -0,0 +1,61 @@ +--- +title: Basic Kit +description: A starter kit menu - one click hands the player a bundle of items plus XP. No cooldown, no rules, just rewards. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The simplest reward menu shape: a description card, a claim button, and a close button. Click the claim button to get a bundle of items, some XP, a sound effect, and a confirmation message. + +## What it teaches + +- Multiple actions in sequence inside one `click { actions { ... } }` block +- The list-form of `itemAdd:` to give several items at once +- `giveXp` action for vanilla XP points +- Delayed `closeMenu: 20` (close after 20 ticks = 1 second, so the player sees the success message before the menu closes) + + + +## How it works + +The claim button's click block runs five actions in order: + +1. **`itemAdd: [ ... ]`** - the list form lets multiple items go into the player's inventory in one shot. +2. **`giveXp: 50`** - adds 50 XP via the configured levels provider (vanilla XP by default). +3. **`sound: ${successSound}`** - plays a success chime from shared templates. +4. **`message:`** - confirmation in chat. +5. **`closeMenu: 20`** - close after 20 ticks (1 second). Using a number instead of `true` adds the delay so the player perceives the actions completing. + +There are no cooldown checks here. Anyone can keep clicking and keep getting kits. To gate this with a cooldown, see the daily kit example which adds a `setVarp` timestamp + an `if` rule comparing it to the current time. + +## Customizing + +Swap any item in the `itemAdd` list with the materials you want. Add as many entries as you need. The `count:` field is optional - default is 1. + +For enchanted items, an `enchantments` field on the item works too: + +```hocon +itemAdd: [ + { + material: DIAMOND_SWORD + enchantments { + sharpness: 5 + unbreaking: 3 + } + } +] +``` + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_kit` in-game. diff --git a/src/content/docs/en/examples/kits-and-rewards/daily-kit-cooldown.mdx b/src/content/docs/en/examples/kits-and-rewards/daily-kit-cooldown.mdx new file mode 100644 index 0000000..da793a4 --- /dev/null +++ b/src/content/docs/en/examples/kits-and-rewards/daily-kit-cooldown.mdx @@ -0,0 +1,89 @@ +--- +title: Daily Kit with Cooldown +description: Daily-claimable kits using temporal variables. The "::1d" suffix on setVarp auto-expires the cooldown after 24 hours. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The classic daily kit pattern: a player claims a kit, gets locked out for 24 hours, then can claim again. Built with temporal variables (auto-expire after a duration), the dual-item pattern (different visual when on cooldown), and template substitution to share kit metadata between both states. + +## What it teaches + +- The temporal variable shorthand: `setVarp: "name::value::1d"` (auto-expires after 1 day) +- The `%varpt_:name%` placeholder for "time remaining" display +- Template substitution: `${defaultKitTemplate}` reused across READY and COOLDOWN states +- Lore extension: `lore: ${defaultKitTemplate.lore} [ ... extra lines ... ]` appends to the template's lore +- The `group:` rule for LuckPerms-based gating on the VIP kit + + + +## How temporal variables work + +`setVarp: "ame_kit_default::1::1d"` parses as: + +- **Variable name**: `ame_kit_default` +- **Value**: `1` (we don't use the value, just the existence) +- **Duration**: `1d` (one day - other units: `s`, `m`, `h`, `d`, `w`) + +After 24 hours, the variable is auto-removed. The `existVarp` rule then evaluates to false, the cooldown item disappears, and the READY item shows again. + +The `%varpt_:ame_kit_default%` placeholder returns the time-to-expire as a human-readable string ("23h 47m" or similar). Useful for showing "available in X" messages. + +## Template substitution + lore extension + +Each kit defines its slot, material, name, and base lore once at the top of the file: + +```hocon +defaultKitTemplate { + slot: 11 + material: WOODEN_SWORD + name: "&aDefault Kit" + lore: [ "&7Includes:", "&7- Leather armor set", ... ] +} +``` + +Both the READY and the COOLDOWN item references this template: + +```hocon +# READY: extends lore with a "Click to claim" line +${defaultKitTemplate} { + lore: ${defaultKitTemplate.lore} [ "", "&aClick to claim" ] + click { ... claim logic ... } +} + +# COOLDOWN: extends lore with the time-remaining line +${defaultKitTemplate} { + material: GRAY_DYE + lore: ${defaultKitTemplate.lore} [ "", "&cAvailable in &e%varpt_:ame_kit_default%" ] + rules { existVarp: "ame_kit_default" } +} +``` + +The `${defaultKitTemplate.lore} [ ... ]` syntax: take the existing lore array, append the new entries. No copy-paste of the base lore. + +When the var doesn't exist, the COOLDOWN item's `existVarp` rule fails, so it doesn't render. The READY item (no rules) takes its place. When the var exists, COOLDOWN renders, READY is suppressed by the dual-slot tie-breaker. + +## Customizing + +Add more kit tiers by: + +1. Add a `xxxKitTemplate` block at the top with `slot:`, `material:`, `name:`, base `lore:`. +2. Add a READY item using the template + `click { ... }` with the kit contents and a unique `setVarp` name. +3. Add a COOLDOWN item using the template + `rules { existVarp: "..." }` with the matching var name. + +For per-tier permission gates, add `rules { group: "tier_name" }` to the click block (or use `permission: "..."` for a simpler check). + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_daily_kit` in-game. +4. Claim the default kit. The item turns gray and shows "Available in 23h 59m..." diff --git a/src/content/docs/en/examples/kits-and-rewards/index.mdx b/src/content/docs/en/examples/kits-and-rewards/index.mdx new file mode 100644 index 0000000..708e88e --- /dev/null +++ b/src/content/docs/en/examples/kits-and-rewards/index.mdx @@ -0,0 +1,18 @@ +--- +title: Kits & Rewards +description: Basic and tiered kits with personal-variable cooldowns and time-based rules. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Basic and tiered kits with personal-variable cooldowns and time-based rules. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/kits-and-rewards/tiered-kits.mdx b/src/content/docs/en/examples/kits-and-rewards/tiered-kits.mdx new file mode 100644 index 0000000..0a0745b --- /dev/null +++ b/src/content/docs/en/examples/kits-and-rewards/tiered-kits.mdx @@ -0,0 +1,35 @@ +--- +title: Tiered Kits +description: Three kit tiers (Default / VIP / Elite), each gated by a permission. No cooldown - the kits can be claimed repeatedly. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A simpler counterpart to [Daily Kit with Cooldown](./daily-kit-cooldown). Same tier idea (default / VIP / Elite) but no temporal-var lockout - the kits can be claimed any time. Useful for "starter packs" or "always-available perks for premium ranks". + + + +## What it teaches + +- Click-level permission gates with `rules { permission: "..." }` +- Composing `denyActions: ${denyNoPerm}` from shared templates for the rejection +- The `itemAdd: [ ... ]` list form for delivering a multi-item bundle + +## Why no dual-item display here? + +This menu shows ALL three kits to everyone. The VIP and Elite kits are visible regardless of whether the player can claim them - the rule check happens on click, not on display. If a regular player clicks VIP, they hit `denyActions` and see the "no permission" message. + +This is intentional UX. Players see what they could unlock - it's a soft sales pitch for the donate ranks. The dual-item pattern (hide locked tiles entirely) is what [Tiered Donate Ranks](../donate/tiered-ranks) uses; pick whichever fits your server. + +## Add a tier + +Copy any kit block, change the slot, name, lore, permission node, and itemAdd contents. For more visual variety, swap `material:` per tier (NETHERITE_SWORD for Mythic etc.). + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_kit_tiers` in-game. +4. The Default kit works for anyone. Grant yourself `abstractmenus.vip` or `abstractmenus.elite` to claim the others. diff --git a/src/content/docs/en/examples/shops/cake-shop.mdx b/src/content/docs/en/examples/shops/cake-shop.mdx new file mode 100644 index 0000000..0e816e7 --- /dev/null +++ b/src/content/docs/en/examples/shops/cake-shop.mdx @@ -0,0 +1,48 @@ +--- +title: Cake Shop +description: A minimal shop selling three items for in-game money. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Cake Shop is the smallest possible shop. Three items, three prices, one click to buy. Read this first if you've never written an AbstractMenus menu. + +## What it teaches + +- The `money` rule for "is the player rich enough?" +- The `takeMoney` action to deduct cost +- The `itemAdd` action to give the bought item +- A `denyActions` block that fires when rules fail +- Reusing shared templates via `${...}` substitution + + + +## How it works + +The menu has size 3 (a single chest row plus the 18-slot border above and below). Three rows of glass-pane border are added by referencing `${border_black}` from the shared templates and assigning a slot range. + +Each item slot defines a `click` block with two parts: + +1. **`rules`** - a list of conditions. Here, `money: 100` means "the player must have at least $100." If any rule fails, the actions in `denyActions` run instead. +2. **`actions`** - the success path. We deduct money, give the item, play a confirmation sound, and send a chat message. + +The `denyActions: ${deny_no_money}` substitution pulls a pre-built action sequence from `_shared/templates.conf` that plays a deny sound and sends a "You don't have enough money" message. Reusable across all economy menus. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_cake_shop` in-game. + +## Next steps + +- See the [paginated shop](./paginated-shop) for handling many items across multiple pages. +- See the [rank-gated shop](./rank-gated-shop) for permission-based pricing. diff --git a/src/content/docs/en/examples/shops/index.mdx b/src/content/docs/en/examples/shops/index.mdx new file mode 100644 index 0000000..c89e4d7 --- /dev/null +++ b/src/content/docs/en/examples/shops/index.mdx @@ -0,0 +1,18 @@ +--- +title: Shops +description: Buy/sell menus showing the money rule, takeMoney action, GeneratedMenu pagination, and NPC vendor patterns. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Buy/sell menus showing the money rule, takeMoney action, GeneratedMenu pagination, and NPC vendor patterns. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/shops/npc-vendor.mdx b/src/content/docs/en/examples/shops/npc-vendor.mdx new file mode 100644 index 0000000..7b91762 --- /dev/null +++ b/src/content/docs/en/examples/shops/npc-vendor.mdx @@ -0,0 +1,65 @@ +--- +title: NPC Vendor +description: A shop that opens when the player right-clicks a specific Citizens NPC. Same shop pattern as Cake Shop, different activator. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickNPC` from Citizens is a popular activator on RPG servers. The player walks up to a vendor NPC, right-clicks, and a shop menu opens. The menu can read NPC context via `%activator_npc_*%` placeholders - this example puts the NPC's name in the menu title for a personal-feeling vendor. + + + +## What it teaches + +- The `clickNPC` activator with a Citizens NPC ID +- The `%activator_npc_name%` placeholder for personalized titles +- How activator-context placeholders propagate through to menu items + +## Setup + +This activator only works when [Citizens](https://www.spigotmc.org/resources/citizens.13811/) is installed and at least one NPC exists with the specified ID. To find a NPC's ID: + +1. Look at the NPC. +2. Run `/npc sel`. +3. Run `/npc` to see selected NPC info, including ID. + +Replace `clickNPC: [1]` with your actual NPC ID. For multiple NPCs that all open the same shop: + +```hocon +clickNPC: [1, 5, 12] +``` + +## NPC context placeholders + +Inside the menu, `%activator_npc_*%` exposes the clicked NPC: + +| Placeholder | Returns | +|---|---| +| `%activator_npc_id%` | Numeric NPC ID | +| `%activator_npc_name%` | NPC's display name | +| `%activator_npc_world%` | World the NPC is in | + +The example uses `%activator_npc_name%` in the title, so the menu title becomes "Trader BlacksmithBob" or whatever the NPC is named. + +## Why this beats `command:` for vendors + +A `/shop` command is universal - everyone uses the same syntax to open the same menu. A right-click on an NPC ties the shop to a specific in-world location and gives the vendor visual personality. Players also can't accidentally open the shop from anywhere - they have to physically walk to the NPC. Better roleplay, less command spam. + +For a hybrid (open via NPC click OR command), put both in the activators block: + +```hocon +activators { + command: "ame_vendor" + clickNPC: [1] +} +``` + +## Try it + +1. Install Citizens. +2. Spawn an NPC: `/npc create Bob` then look at it, `/npc sel`, `/npc` to confirm ID. +3. Drop the bundle into `plugins/AbstractMenus/menus/example/`. Update `clickNPC: [1]` to your NPC's ID. +4. `/am reload`. +5. Right-click the NPC. diff --git a/src/content/docs/en/examples/shops/paginated-shop.mdx b/src/content/docs/en/examples/shops/paginated-shop.mdx new file mode 100644 index 0000000..737761c --- /dev/null +++ b/src/content/docs/en/examples/shops/paginated-shop.mdx @@ -0,0 +1,58 @@ +--- +title: Multi-Category Shop +description: A hub menu with category tiles that open dedicated sub-shops, each demonstrating the buy pattern. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +When a shop has more items than fit comfortably on one page, the natural pattern is a hub plus per-category sub-shops. This example shows the structure with three menus in one file: a hub and two categories. + +## What it teaches + +- Multi-menu files using the `menus { ... }` wrapper +- Cross-menu navigation via `openMenu` +- Repeating the buy pattern across categories without duplication +- Mixing static borders with shop items +- The `flags: HIDE_ATTRIBUTES` shortcut for tool/weapon icons + + + +## How it works + +The file uses the `menus { ... }` wrapper to declare three named menus: + +- `ame_shop` is the main hub. It carries the `command: "ame_shop"` activator and shows category tiles. +- `ame_shop_food` and `ame_shop_tools` are sub-shops opened from the hub. + +Each tile in the hub is just an item with `click { openMenu: "ame_shop_food" }`. No money rule, no actions on top of the navigation. Sub-shops are reached by name reference. + +Within a sub-shop, the items follow the same pattern as the [Cake Shop](./cake-shop): `rules { money: ... }`, an `actions {}` block that takes the money and gives the item, and `denyActions: ${denyNoMoney}` from shared templates for the failure path. + +The back button uses `openMenu: "ame_shop"` to return to the hub. There's no built-in "back" verb. Navigating menus is just calling `openMenu` with the previous menu's name. + +## Scaling out + +Adding a new category is mechanical: + +1. Add a new tile to the hub items list with `click { openMenu: "ame_shop_" }`. +2. Copy any sub-shop block (e.g. `ame_shop_food`), rename it to `ame_shop_`, swap the items. +3. Reload the menu with `/am reload`. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_shop` in-game. + +## Next steps + +- See [Cake Shop](./cake-shop) for the simplest single-menu shop. +- See the [generation docs](/docs/en/advanced/generation/) for pagination over a dynamic catalog (when items come from a code-side source like online players or worlds). diff --git a/src/content/docs/en/examples/shops/rank-gated-shop.mdx b/src/content/docs/en/examples/shops/rank-gated-shop.mdx new file mode 100644 index 0000000..413e317 --- /dev/null +++ b/src/content/docs/en/examples/shops/rank-gated-shop.mdx @@ -0,0 +1,60 @@ +--- +title: Rank-Gated Shop +description: A shop where VIPs see different prices than regular players. The dual-item HAS/NO_PERM pattern from real production servers. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Many real shops show different prices for different ranks - VIPs get a discount, donors see exclusive items, etc. The implementation uses two items at the same slot: the first carries a permission rule, the second is the fallback. + + + +## What it teaches + +- The dual-item display pattern with a permission rule +- Per-rank pricing without `if` rule complexity +- Reusable click handlers via `${discountClick}` / `${regularClick}` substitution +- How rule short-circuiting and slot ordering interact + +## How dual-item works + +Two items declared at the same slot: + +```hocon +# Variant A: shown only to VIPs +{ slot: 13, name: "Golden Apple (VIP price)", rules { permission: "...vip" }, click: ${discountClick} } + +# Variant B: fallback for everyone else +{ slot: 13, name: "Golden Apple", click: ${regularClick} } +``` + +When the menu renders, the plugin walks the items list. For slot 13 it finds variant A, checks its rules, and either: + +- Uses A if the rule passes (VIP sees the discounted version) +- Skips A and looks for the next definition at slot 13. Finds B. B has no rules. B renders. + +This is THE production pattern from real-world shops. It scales to many ranks (4-tier donate stores, etc.) by having one variant per rank, in priority order, with the highest-tier rule first. + +## Why this beats `if` for rank checks + +The `if` rule could express the same logic, but with two extra problems: + +1. The displayed item itself wouldn't change - just the click action. Visual differentiation requires dual items anyway. +2. `if` is parsed as an expression on every render. Permission-based dual items are cheaper. + +Use `if` when comparing numeric/string values that can't be expressed as a built-in rule (level math, custom var values). Use dual-item permission for "this player is in tier X". + +## Customizing for more tiers + +Add more variants in priority order. Mythic > Donor > VIP > regular: + +```hocon +{ slot: 13, ..., rules { permission: "myplugin.mythic" }, click: ${mythicClick} } +{ slot: 13, ..., rules { permission: "myplugin.donor" }, click: ${donorClick} } +{ slot: 13, ..., rules { permission: "myplugin.vip" }, click: ${vipClick} } +{ slot: 13, ..., click: ${regularClick} } # fallback - no rules +``` + +Order matters: highest-priority tier first. The first matching variant wins. diff --git a/src/content/docs/en/examples/shops/sell-and-buy-shop.mdx b/src/content/docs/en/examples/shops/sell-and-buy-shop.mdx new file mode 100644 index 0000000..6a6dd21 --- /dev/null +++ b/src/content/docs/en/examples/shops/sell-and-buy-shop.mdx @@ -0,0 +1,69 @@ +--- +title: Sell & Buy Shop +description: One menu, two operations - left-click buys an item, right-click sells it. Asymmetric prices simulate a trader margin. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A trader-style shop where each tile lets the player buy OR sell the same item, depending on which mouse button they click. Buy price is higher than sell price (the trader takes a margin). Demonstrates click-type discrimination at the same slot. + + + +## What it teaches + +- `click { left { ... } right { ... } }` for per-button-type behavior +- Pairing the `money` rule with `takeMoney` for the buy path +- Pairing the `inventoryItems` rule with `itemRemove` + `giveMoney` for the sell path +- Asymmetric trader pricing (buy 20, sell 15) + +## How click types work + +A `click {}` block can have multiple sub-blocks, one per mouse-button type: + +```hocon +click { + left { rules { money: 20 }, actions { takeMoney: 20, itemAdd { ... } } } + right { rules { inventoryItems: [...] }, actions { itemRemove: [...], giveMoney: 15 } } +} +``` + +When the player left-clicks, only the `left` sub-block runs. When they right-click, only `right`. This is different from a flat `click { rules: ..., actions: ... }` which fires for any click type. + +Available click types: `left`, `right`, `shift_left`, `shift_right`, `drop`. Add more sub-blocks for more button types - common pattern is `left` for primary action, `shift_left` for "do it 8x" or "advanced variant". + +## Why asymmetric prices + +Trader plugins usually buy items from players cheaper than they sell them. The spread (buy 20 - sell 15 = 5 margin) represents the trader's cut. This prevents players from infinitely cycling items through the shop for profit. + +For an arbitrage-proof shop: + +- buy_price > sell_price (always) +- buy_price > sell_price for any chain through other shops too + +For a "fair price" UI (no margin), set both to the same value. + +## Inventory check on sell + +The right-click handler uses the `inventoryItems` rule to verify the player has the item to sell: + +```hocon +right { + rules { inventoryItems: [{ material: WHEAT, count: 16 }] } + actions { + itemRemove: [{ material: WHEAT, count: 16 }] + giveMoney: 15 + } +} +``` + +If the player doesn't have 16 wheat, the rule fails, the actions don't fire, and the local `denyActions` shows a "you don't have enough to sell" message instead. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 1000`. +4. Type `/ame_buysell` in-game. +5. Left-click wheat to buy. Right-click to sell back. diff --git a/src/content/docs/en/examples/snippets/action-bulk-delay.mdx b/src/content/docs/en/examples/snippets/action-bulk-delay.mdx new file mode 100644 index 0000000..f90eee5 --- /dev/null +++ b/src/content/docs/en/examples/snippets/action-bulk-delay.mdx @@ -0,0 +1,40 @@ +--- +title: "Actions: bulk + delay" +description: Two action wrappers - bulk repeats action groups in sequence, delay defers actions by ticks. +sidebar: + order: 7 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`bulk` and `delay` are meta-actions that wrap other actions. `bulk` runs each entry in its list as a separate action group (so you can repeat actions of the same name). `delay` waits N ticks before running its wrapped block. + + + +## bulk + +```hocon +bulk: [ + { message: "&e3..." } + { message: "&62..." } + { message: "&c1..." } +] +``` + +Each list entry is a self-contained action group (same shape as the parent `actions {}`). All entries fire in order, immediately, on the same tick. Useful when you need three messages in sequence (you can't have three `message:` keys at the top of one actions block - HOCON object keys must be unique). + +## delay + +```hocon +delay { + delay: 60 + actions { + sound: ${successSound} + message: "&aBoom!" + } +} +``` + +The outer `delay:` is in ticks (60 = 3 seconds). After that delay, the inner `actions {}` runs as a normal action group. Useful for countdowns, scheduled rewards, or staging visual effects. + +Inside `delay`, you can have rules and denyActions too - the delayed block is a full self-contained actions context. diff --git a/src/content/docs/en/examples/snippets/action-give-item.mdx b/src/content/docs/en/examples/snippets/action-give-item.mdx new file mode 100644 index 0000000..76799ba --- /dev/null +++ b/src/content/docs/en/examples/snippets/action-give-item.mdx @@ -0,0 +1,37 @@ +--- +title: "Action: itemAdd" +description: Give the player one or more items. Supports custom names, lore, enchantments, and any other item field. +sidebar: + order: 6 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`itemAdd` adds an item (or list of items) to the player's inventory. The shape inside is the full item-definition spec - everything that works on a regular menu item works here too: material, name, lore, enchantments, custom NBT. + + + +## Usage + +```hocon +actions { + itemAdd { + material: WOODEN_PICKAXE + name: "&aStarter Pickaxe" + } +} +``` + +Multiple items via list: + +```hocon +actions { + itemAdd: [ + { material: BREAD, count: 16 } + { material: APPLE, count: 4 } + { material: GOLDEN_APPLE } + ] +} +``` + +If the player's inventory is full, items overflow to the ground (vanilla behavior). To check inventory space first, gate the click with the `freeSlot` or `freeSlotCount` rules. diff --git a/src/content/docs/en/examples/snippets/action-rand-actions.mdx b/src/content/docs/en/examples/snippets/action-rand-actions.mdx new file mode 100644 index 0000000..29c8c2f --- /dev/null +++ b/src/content/docs/en/examples/snippets/action-rand-actions.mdx @@ -0,0 +1,37 @@ +--- +title: "Action: randActions" +description: Pick one entry at random from a list of action groups. Each entry is independent. +sidebar: + order: 8 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`randActions` takes a list of action groups and picks exactly one at random to execute. Each entry is a full self-contained group - sounds, messages, items, anything goes. + + + +## Usage + +```hocon +randActions: [ + { message: "Outcome A" } + { message: "Outcome B" } + { message: "Outcome C" } +] +``` + +Each entry has equal probability by default. To **weight** outcomes, repeat them - an entry appearing twice is twice as likely: + +```hocon +randActions: [ + { message: "Common outcome" } + { message: "Common outcome" } + { message: "Common outcome" } + { message: "Rare outcome" } +] +``` + +Above: 3-in-4 chance of "Common", 1-in-4 chance of "Rare". + +For one-of-N display (which item shows up) instead of one-of-N action, use the `chance` rule on items - see [Rule: chance](./rule-chance). diff --git a/src/content/docs/en/examples/snippets/activator-clickitem.mdx b/src/content/docs/en/examples/snippets/activator-clickitem.mdx new file mode 100644 index 0000000..caf3316 --- /dev/null +++ b/src/content/docs/en/examples/snippets/activator-clickitem.mdx @@ -0,0 +1,51 @@ +--- +title: "Activator: clickItem" +description: Open a menu when the player right-clicks a specific item in their hand. No command needed. +sidebar: + order: 9 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickItem` opens the menu when the player right-clicks an item that matches the spec while holding it. Useful for "Magic Wand" UIs, custom-named tools that pop up menus, compass-based navigation, etc. + + + +## Usage + +```hocon +activators { + clickItem { + material: NETHER_STAR + name: "&6Menu Wand" + } +} +``` + +All listed properties must match for the activator to fire. The example above requires both the material AND the exact item name. If the player right-clicks a NETHER_STAR with a different name, nothing happens. + +For just material match, drop the `name:` field. For multiple item triggers, use a list: + +```hocon +activators { + clickItem: [ + { material: COMPASS } + { material: NETHER_STAR, name: "&6Menu Wand" } + ] +} +``` + +The plugin sets `%activator_item_*%` placeholders inside the menu, exposing the clicked item's data. See [Activator context](/docs/en/general/activators/#activator-context-and-placeholders) for the full extractor table. + +## Other "by-the-world" activators + +Same shape, different sources: + +- `clickBlock` - click a specific block at known coordinates +- `clickBlockType` - click any block of a given material +- `clickEntity` / `shiftClickEntity` - click an entity (mob, armor stand, item frame) +- `clickNPC` - click a Citizens NPC by id +- `regionJoin` / `regionLeave` - WorldGuard region triggers +- `button` / `lever` / `plate` - vanilla redstone interactables + +Mix and match in the same `activators {}` block - a menu can be opened via command, item-click, AND region-enter, all at once. diff --git a/src/content/docs/en/examples/snippets/index.mdx b/src/content/docs/en/examples/snippets/index.mdx new file mode 100644 index 0000000..cf229fd --- /dev/null +++ b/src/content/docs/en/examples/snippets/index.mdx @@ -0,0 +1,18 @@ +--- +title: Snippets +description: Tiny single-feature demos for each rule, action, and activator type. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Tiny single-feature demos for each rule, action, and activator type. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/snippets/inventory-crafting.mdx b/src/content/docs/en/examples/snippets/inventory-crafting.mdx new file mode 100644 index 0000000..f1bc382 --- /dev/null +++ b/src/content/docs/en/examples/snippets/inventory-crafting.mdx @@ -0,0 +1,79 @@ +--- +title: Inventory Crafting +description: Craft items by consuming ingredients from the player's inventory. Demonstrates the inventoryItems rule paired with itemRemove + itemAdd actions. +sidebar: + order: 11 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A workbench-style menu that "crafts" items: the player must hold the recipe ingredients in their inventory, clicking the recipe consumes them and gives back the crafted result. Different mechanism from drag-and-drop slots - this checks the player's regular inventory. + +## What it teaches + +- The `inventoryItems` rule for "does the player have these items" +- `itemRemove` to consume the matching items +- `itemAdd` to give the crafted output +- Defining recipes as named blocks at file scope, referenced via `${name}` substitution + + + +## How it works + +Each recipe defines an `output` (the crafted item) and a `requirements` list (the ingredients): + +```hocon +helmetRecipe { + output { + material: LEATHER_HELMET + name: "&aHunter's Hat" + enchantments { durability: 2 } + } + requirements: [ + { material: LEATHER, count: 5 } + ] +} +``` + +The click block references both: + +```hocon +click { + rules { inventoryItems: ${helmetRecipe.requirements} } + actions { + itemRemove: ${helmetRecipe.requirements} + itemAdd: ${helmetRecipe.output} + ... + } + denyActions: ${denyNotEnoughItems} +} +``` + +This pattern keeps the recipe data in one place: the rule, the consumption, and the output all reference the same `helmetRecipe` block. Adding or editing a recipe means editing one block, not three. + +## Inventory crafting vs drag-and-drop + +Two distinct AbstractMenus patterns for "crafting": + +- **Inventory crafting** (this example): items stay in the player's normal inventory. The `inventoryItems` rule looks at the inventory, `itemRemove` deducts from it. Player never moves items. +- **Drag-and-drop crafting**: the menu has draggable slots. Player physically drags items into them. Uses the `placeItem` action, `placedItem` rule, and the `draggable` field on items. See the [drag-and-drop docs](/docs/en/advanced/drag-and-drop/) for the alternative pattern. + +This snippet uses the inventory-crafting pattern because it composes well with shop-like UIs and doesn't need a custom slot configuration. + +## Adding more recipes + +Define a new `xxxRecipe` block at file scope with `output {}` and `requirements: [...]`. Add an item in the `items` list referencing it the same way the helmet does. Three places get the recipe: the display item, the rule, the actions. Keeping them all `${xxxRecipe.something}` means recipes can't drift out of sync. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Get 5 leather in your inventory. +4. Type `/ame_craft` in-game. +5. Click the helmet recipe. Leather consumed, hat appears. diff --git a/src/content/docs/en/examples/snippets/rule-and-or.mdx b/src/content/docs/en/examples/snippets/rule-and-or.mdx new file mode 100644 index 0000000..258be5f --- /dev/null +++ b/src/content/docs/en/examples/snippets/rule-and-or.mdx @@ -0,0 +1,48 @@ +--- +title: "Rules: and / or" +description: Logical wrappers for combining multiple rules. and = all match. or = at least one matches. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The default `rules { ... }` block already ANDs everything inside. The explicit `and` wrapper is mostly useful inside `or` (since you can't otherwise nest AND inside OR). The `or` wrapper passes when any of its child rules pass. + + + +## Usage + +```hocon +rules { + or { + permission: "vip" + money: 1000 + } +} +``` + +This passes if the player has the VIP permission **OR** has $1000+. Either condition is enough. + +```hocon +rules { + or: [ + { + and { + permission: "vip" + money: 100 + } + } + { + and { + permission: "admin" + gamemode: CREATIVE + } + } + ] +} +``` + +Nested form: passes if (VIP and $100+) OR (admin and creative mode). Each entry in the `or:` list is a separate rule group. + +There's also `oneof` (matches first true entry, short-circuits) - see the [Item Enhancer example](../state-and-vars/item-enhancer) for that. diff --git a/src/content/docs/en/examples/snippets/rule-chance.mdx b/src/content/docs/en/examples/snippets/rule-chance.mdx new file mode 100644 index 0000000..c49068a --- /dev/null +++ b/src/content/docs/en/examples/snippets/rule-chance.mdx @@ -0,0 +1,27 @@ +--- +title: "Rule: chance" +description: Probabilistic display - the item shows up only N% of the time the menu is opened. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`chance: 30` means "33 times out of 100, this rule passes". Useful for rare loot displays, lottery tickets, or any "sometimes you see X" UI. + + + +## Usage + +```hocon +{ + slot: 4 + material: NETHER_STAR + name: "&dRare item" + rules { chance: 30 } +} +``` + +The rule rolls when the menu is rendered (on open and on each `updateInterval` tick if set). For a fallback item that shows the OTHER 70% of the time, add a second item at the same slot with no rules - the dual-item pattern handles it. + +For weighted random outcomes after a click (not display), use `randActions` instead - see [Action: randActions](./action-rand-actions). diff --git a/src/content/docs/en/examples/snippets/rule-if-placeholder.mdx b/src/content/docs/en/examples/snippets/rule-if-placeholder.mdx new file mode 100644 index 0000000..7194ed3 --- /dev/null +++ b/src/content/docs/en/examples/snippets/rule-if-placeholder.mdx @@ -0,0 +1,30 @@ +--- +title: "Rule: if (placeholder expression)" +description: Compare placeholder values with operators like >=, ==, ||, &&. The general-purpose conditional rule. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +When a built-in rule like `permission` or `money` doesn't fit, the `if` rule lets you write expressions over any placeholder. Supports `>`, `<`, `>=`, `<=`, `==`, `!=`, `===`, `!==`, `&&`, `||`, plus parentheses. + + + +## Usage + +```hocon +rules { + if: "%player_level% >= 10" +} +``` + +```hocon +rules { + if: "(%player_lvl% == 5 || %player_lvl% == 10) && %player_world% == survival" +} +``` + +Inside the expression, placeholders unwrap to their string or numeric values. Bare strings (no quotes needed) are compared with the operators. Use `===`/`!==` for case-insensitive matching of names. + +For complex math, use the `js` rule instead - it runs JavaScript expressions. `if` is faster and recommended for plain logic. diff --git a/src/content/docs/en/examples/snippets/rule-money.mdx b/src/content/docs/en/examples/snippets/rule-money.mdx new file mode 100644 index 0000000..8b896fe --- /dev/null +++ b/src/content/docs/en/examples/snippets/rule-money.mdx @@ -0,0 +1,29 @@ +--- +title: "Rule: money" +description: Gate a click on the player having enough currency. Pair with takeMoney to actually charge. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The `money` rule asks the configured economy provider "does this player have at least N?". If yes, the click's actions run; if no, `denyActions` runs. + + + +## Usage + +```hocon +click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { ... } + } + denyActions: ${denyNoMoney} +} +``` + +The rule only checks balance - it doesn't deduct. The `takeMoney` action does the actual charge. This separation lets you use `money` as a pure visual gate (showing different items based on balance) without spending the player's coins. + +Use the object form `money: { amount: 50, provider: "vault" }` to pin to a specific economy provider when multiple are registered. diff --git a/src/content/docs/en/examples/snippets/rule-permission.mdx b/src/content/docs/en/examples/snippets/rule-permission.mdx new file mode 100644 index 0000000..8a4dc08 --- /dev/null +++ b/src/content/docs/en/examples/snippets/rule-permission.mdx @@ -0,0 +1,24 @@ +--- +title: "Rule: permission" +description: Gate a click on a single permission node. The minimal demo of the permission rule. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +The permission rule is the most-used gate in any menu - "only let the player do this if they have the permission." Pair it with `denyActions` for a friendly rejection. + + + +## Usage + +```hocon +click { + rules { permission: "myplugin.admin" } + actions { ... } + denyActions: ${denyNoPerm} +} +``` + +The rule passes if the player has the named permission node. To gate by group instead of permission, swap to the `group` rule (works with LuckPerms). To invert (allow only those WITHOUT the permission), prefix with `-`: `-permission: "..."`. diff --git a/src/content/docs/en/examples/snippets/template-substitution.mdx b/src/content/docs/en/examples/snippets/template-substitution.mdx new file mode 100644 index 0000000..55e7ef9 --- /dev/null +++ b/src/content/docs/en/examples/snippets/template-substitution.mdx @@ -0,0 +1,54 @@ +--- +title: "HOCON template substitution" +description: Define an item shape once, instantiate it many times with per-use overrides. The killer feature of HOCON. +sidebar: + order: 10 +--- + +import MenuExample from '@components/MenuExample.astro'; + +HOCON supports `${name}` substitution - reference a previously-defined block by name. When followed by `{ ... overrides ... }`, the override merges on top of the substituted value. This is what makes the shared `_shared/templates.conf` and per-menu local templates work. + + + +## Usage + +```hocon +# Define once +prizeTile { + material: NETHER_STAR + lore: ["&7Click for a prize."] + click { actions { itemAdd { material: DIAMOND } } } +} + +# Use multiple times with overrides +items: [ + ${prizeTile} { slot: 2, name: "&aCommon Prize" } + ${prizeTile} { slot: 4, name: "&6Rare Prize" } + ${prizeTile} { slot: 6, name: "&dEpic Prize" } +] +``` + +Each instance starts as a copy of `prizeTile` (material, lore, click), then the override block adds slot and name. The result is three different items sharing structure but differing in identifying fields. + +## Merge semantics + +`${A} { B }` does a **deep object merge** - keys from B override keys from A, but nested objects merge recursively. So if A has `click.actions.sound: ${clickSound}` and B has `click.actions.message: "Hi"`, the merged result has both: `click.actions.{sound: ..., message: "Hi"}`. + +For lists, the override REPLACES the original list - it doesn't append. To extend a lore list: + +```hocon +${prizeTile} { + lore: ${prizeTile.lore} [ "&7Extra line" ] +} +``` + +The `${prizeTile.lore}` substitution gets the original lore array, then `[...]` appends new entries. See the [Daily Kit example](../kits-and-rewards/daily-kit-cooldown) for this pattern in action. + +## When to template + +- Item shape repeated 3+ times: extract a template +- A click handler used in multiple items: extract `clickFoo {...}` and reference via `click: ${clickFoo}` +- Cost or price data shared between display item and rule check: define once at file scope, reference in both spots + +If something is used twice and inlined still reads clearly, leave it inlined. Templates pay off when they reduce edit surface for repeated logic. diff --git a/src/content/docs/en/examples/state-and-vars/bungee-status.mdx b/src/content/docs/en/examples/state-and-vars/bungee-status.mdx new file mode 100644 index 0000000..b7d9d0d --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/bungee-status.mdx @@ -0,0 +1,67 @@ +--- +title: Bungee Status +description: A server picker for BungeeCord/Velocity networks. Each tile shows live online counts and connects the player on click. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Networks running BungeeCord or Velocity expose per-server online counts via plugin messages. AbstractMenus surfaces them as `%bungee_%` placeholders. This menu uses them to build a "server picker" with live counts and click-to-connect tiles. + +## What it teaches + +- The `bungeeConnect` action for inter-server transfer +- The `%bungee_%` placeholder for live online counts +- Using `glow: true` to make a featured tile stand out (lobby in this case) +- Pairing `updateInterval` with bungee placeholders to keep counts fresh + + + +## How it works + +Each tile is a regular item with one custom action: `click { bungeeConnect: "lobby" }`. When clicked, the plugin sends a BungeeCord transfer plugin message and the proxy sends the player to the named backend server. + +The `%bungee_lobby%` placeholder asks the BungeeCord listener "how many players are on the `lobby` backend right now?" and renders the count. With `updateInterval: 40` (2 seconds), the count refreshes while the menu is open. + +For the count to work: + +1. The server running AbstractMenus needs `bungeeCord: true` in `plugins/AbstractMenus/config.conf`. +2. The proxy needs the same server name in its config (the `lobby` in `bungeeConnect: "lobby"` must match the proxy's registered name). +3. BungeeCord plugin messaging must be enabled on both ends. + +## Adding more servers + +Each tile is independent. Copy any block, change the slot, name, lore, and the two server name references (`bungeeConnect` + the placeholder). Real backend names go in instead of `lobby` / `survival` etc. + +For visual variety, swap `material:` per game mode (TNT for "TNT Games", IRON_SWORD for "Battle Royale", etc). + +## Showing online/offline status + +To gate a tile by whether the server is reachable, use the `bungeeIsOnline` rule: + +```hocon +{ + slot: 10 + ... + rules { + bungeeIsOnline: "lobby" + } +} +``` + +If lobby is offline, the item won't render. Add a fallback "offline" item at the same slot with no rules and a different look (gray dye, "Server is offline" text) to use the dual-item pattern from [Counter & Toggle](./counter-toggle). + +## Try it + +After installing the example pack: + +1. Run a BungeeCord/Velocity proxy with backends named `lobby`, `survival`, `sw`, `bb`. +2. Set `bungeeCord: true` in `plugins/AbstractMenus/config.conf`. +3. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +4. `/am reload`. +5. Type `/ame_servers` in-game. diff --git a/src/content/docs/en/examples/state-and-vars/counter-toggle.mdx b/src/content/docs/en/examples/state-and-vars/counter-toggle.mdx new file mode 100644 index 0000000..c97f59a --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/counter-toggle.mdx @@ -0,0 +1,74 @@ +--- +title: Counter & Toggle +description: Personal variables in action - a clickable counter and a stateful toggle, both persisted between sessions. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Two of the most common state primitives in any GUI: a numeric counter that goes up and down, and a binary toggle. Both demo the per-player variable system (`varp`) and the dual-item pattern for conditional display. + +## What it teaches + +- `incVarp` / `decVarp` / `removeVarp` for numeric mutations +- `setVarp` with the `"name::value"` shorthand +- The `%varp_:name:default%` placeholder syntax +- The `existVarp` rule for "does this variable exist for this player" +- The dual-item pattern: two items at the same slot, the first with rules, the second as fallback +- `refreshMenu: true` so the display updates without closing and reopening + + + +## How the counter works + +The counter uses three actions and a placeholder: + +- `incVarp: "ame_counter"` adds 1 to the variable. If it doesn't exist, the plugin auto-initializes it to 0 first. +- `decVarp: "ame_counter"` subtracts 1. +- `removeVarp: "ame_counter"` deletes the variable - effectively a reset to "no counter". + +The display item shows `%varp_:ame_counter:0%`. The `:0` suffix is the default value: when the variable doesn't exist, the placeholder renders as `0` instead of an empty string. + +Every click action ends with `refreshMenu: true` so the display item updates immediately. Without this, the counter would only refresh when the menu's auto-update interval fires (or never, if `updateInterval` is unset). + +## How the toggle works + +The toggle uses the **dual-item pattern**: two items declared at the same slot, where the first one renders only when its rules pass, and the second one is the fallback. + +```hocon +# Visible when toggle is set +{ slot: 15, material: LIME_DYE, rules { existVarp: "ame_toggle" }, ... } + +# Visible when toggle is NOT set +{ slot: 15, material: GRAY_DYE, ... } # no rules = always passes +``` + +When the player has the `ame_toggle` variable set, the green item shows and clicking it removes the variable. When the variable doesn't exist, the gray item shows and clicking it sets the variable to "1" (the value doesn't matter for an existence check, just that the variable exists). + +This pattern is heavily used for things like "give item / already claimed", "VIP price / regular price", "muted / unmuted" - any binary state where the player should see different visuals. + +## Customizing + +To change the variable name, replace `ame_counter` and `ame_toggle` everywhere - in the actions, the `existVarp` rule, and the placeholder. Pick names with a server-specific prefix to avoid collisions with other menus. + +For a counter with a step bigger than 1, use the object form: + +```hocon +incVarp: [ + { name: "ame_counter", amount: 5 } +] +``` + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_counter` in-game. +4. Click around. The counter persists - close the menu, reopen, the value is still there. diff --git a/src/content/docs/en/examples/state-and-vars/global-stats-board.mdx b/src/content/docs/en/examples/state-and-vars/global-stats-board.mdx new file mode 100644 index 0000000..157f639 --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/global-stats-board.mdx @@ -0,0 +1,56 @@ +--- +title: Global Stats Board +description: Live-updating dashboard with player statistics and server status. Demonstrates placeholders + updateInterval refresh. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A read-only menu showing live data via PlaceholderAPI. The top row displays per-player stats (kills, deaths, hours played), the bottom row shows server-wide info (online count, time, TPS, current world). Values refresh every second thanks to `updateInterval: 20` (20 ticks = 1 second). + +## What it teaches + +- The `updateInterval` field for live menu refresh +- PlaceholderAPI integration: `%statistic_*%`, `%server_*%`, `%player_*%` +- Mixing per-player and server-wide data in one menu +- Custom skull textures via the `texture:` field on PLAYER_HEAD + + + +## How updateInterval works + +`updateInterval: 20` tells the menu to re-evaluate placeholders every 20 server ticks (1 second). On each tick, every item's `name:` and `lore:` get re-rendered with current placeholder values. Players see counts increment in real time without closing/reopening. + +For static menus this field is unnecessary - they only render once on open. For dashboards with constantly-changing values (stats, online count), it's essential. + +Set this too low (e.g. `1` = every tick = 50ms) and you'll see CPU spikes from placeholder evaluation. `20` (1s) or `40` (2s) is the sweet spot for human-readable refresh. + +## Placeholder requirements + +This menu uses PlaceholderAPI placeholders from several expansions: + +- **`%statistic_*%`** - PlaceholderAPI's [Statistic expansion](https://api.extendedclip.com/expansions/statistic/). Install with `/papi ecloud download Statistic`. +- **`%server_*%`** - PlaceholderAPI's built-in server placeholders. No extra expansion needed. +- **`%player_*%`** - PlaceholderAPI's built-in player placeholders. No extra expansion needed. + +If a placeholder isn't resolved, it'll render as the literal `%xxx%` string in the menu - that's a sign the expansion isn't installed. + +## Adding more stats + +Pick a placeholder from the [PlaceholderAPI cloud](https://api.extendedclip.com/all/) and drop it into a new item's `lore`. The menu will pick up the live value on next refresh. + +For per-player vars from this plugin (not PAPI), use the `%varp_:name:default%` form - see the [Counter & Toggle](./counter-toggle) example. + +## Try it + +After installing the example pack: + +1. Install [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) and the Statistic expansion (`/papi ecloud download Statistic`). +2. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +3. `/am reload`. +4. Type `/ame_stats` in-game. Watch the values refresh once per second. diff --git a/src/content/docs/en/examples/state-and-vars/index.mdx b/src/content/docs/en/examples/state-and-vars/index.mdx new file mode 100644 index 0000000..63c0ef3 --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/index.mdx @@ -0,0 +1,18 @@ +--- +title: State & Variables +description: Counters, vote cooldowns, live stat boards using personal and global variables. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Counters, vote cooldowns, live stat boards using personal and global variables. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/state-and-vars/item-enhancer.mdx b/src/content/docs/en/examples/state-and-vars/item-enhancer.mdx new file mode 100644 index 0000000..b913e91 --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/item-enhancer.mdx @@ -0,0 +1,65 @@ +--- +title: Item Enhancer +description: Drop a vanilla item, get an upgraded version. Demonstrates drag-and-drop slots paired with the oneof rule for conditional transformations. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A "fixed-table" enhancement menu: stone becomes coal ore, apple becomes golden apple, wooden sword becomes diamond sword, wooden axe becomes diamond axe. Built around the `oneof` rule which short-circuits on the first matching condition - the perfect fit for "switch on item type, do different thing". + +## What it teaches + +- The `oneof` rule type and its short-circuit "first match wins" semantics +- Per-branch actions inside oneof entries (each entry has its own `actions {}`) +- Drag-and-drop slots and the `placedItem` rule for input matching +- Combining drag-and-drop primitives (`placeItem`, `removePlaced`, `setButton`) into a transformation pipeline +- Templates for reusable rule and action shapes (`${inputSlotRule}`, `${outputAction}`) + + + +## Why state-and-vars? + +This example is filed under state-and-vars because the drag-and-drop machinery is itself a form of menu state - the items physically held in slots 2 and 6 are session-local "variables" of the menu. It doesn't use `setVar`/`setVarp`, but the conceptual pattern (read the slot's state, branch on it, mutate it) is the same. + +## How `oneof` works + +`oneof` is a logical rule wrapper that takes a list of entries. It walks the list top-to-bottom and stops on the first whose own rule matches. The matching entry's `actions {}` block is then run. + +```hocon +oneof: [ + { placedItem: { slot: 2, material: STONE }, actions { placeItem: { ..., material: COAL_ORE } } } + { placedItem: { slot: 2, material: APPLE }, actions { placeItem: { ..., material: GOLDEN_APPLE } } } + ... +] +``` + +If none of the entries match, the entire `oneof` evaluates false. The outer click block then runs `denyActions` instead of the success path. + +This is cleaner than nested `if`/`and`/`or` for switch-like logic. + +## Adding a new recipe + +Append to the `oneof` list in `enhanceRules`: + +```hocon +{ placedItem: ${inputSlotRule} { material: COAL } + actions { placeItem: ${outputAction} { material: DIAMOND } } } +``` + +The `${inputSlotRule}` and `${outputAction}` substitutions keep the slot numbers in one place - if you reorganize the menu layout, only the two anchor blocks change, not every recipe entry. + +## Try it + +After installing the example pack: + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_enhancer` in-game. +4. Drag a stone block into the left slot. +5. Click "Enhance!". A coal ore appears in the right slot. diff --git a/src/content/docs/en/examples/state-and-vars/vote-cooldown.mdx b/src/content/docs/en/examples/state-and-vars/vote-cooldown.mdx new file mode 100644 index 0000000..63a6e6a --- /dev/null +++ b/src/content/docs/en/examples/state-and-vars/vote-cooldown.mdx @@ -0,0 +1,56 @@ +--- +title: Vote Cooldown +description: A daily-vote button that combines a global counter with a per-player 24-hour cooldown using temporal variables. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A vote menu with two pieces of state: a global counter that everyone bumps up (server's all-time vote count) and a per-player temporal variable that locks them out for 24 hours after voting. Pairs `incVar` (global) with `setVarp ::1d` (personal temporal) for the typical "vote once a day" pattern. + + + +## What it teaches + +- `incVar` for global counters that all players share +- `setVarp` with the `::1d` temporal duration suffix +- The `%var_:name:default%` placeholder for global vars (note: same colon-default syntax as varp) +- Pairing global and personal vars in the same click handler +- Dual-item display for the vote button (ON_COOLDOWN vs READY states) + +## Global vs personal + +Two var families: + +- **`var_*` / setVar / incVar** - global, server-wide. Everyone reads and writes the same value. Used here for the all-time vote total. +- **`varp_*` / setVarp / incVarp** - per-player. Each player has their own copy. Used here for the cooldown lockout. + +When the click fires, both are touched in the same actions block: + +```hocon +actions { + incVar: "ame_vote_total" # global counter +1 + setVarp: "ame_vote_cd::1::1d" # per-player 24h temporal lockout + ... +} +``` + +The global counter increments forever. The per-player cooldown auto-expires after 1 day, opening the vote button again. + +## Real vote integration + +This example is a self-contained demo. To wire it to actual server voting (Votifier, MinecraftPocket, etc.): + +1. Don't have the player click the vote button - the external vote site/proxy fires the reward. +2. Use the same `incVar` + `setVarp` pattern inside whatever Bukkit listener handles incoming votes. +3. Set `iconmenu.canclaim` perm or similar gate, then this menu becomes a "claim your daily vote reward" button rather than a "vote" button. + +The plugin docs page on variables covers persistence options if you need cross-restart guarantees on the global counter. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Type `/ame_vote` in-game. +4. Click the lime "Vote!" button. Counter goes up, the button turns gray with a 23h 59m countdown. diff --git a/src/content/docs/en/examples/world-integrations/block-as-button.mdx b/src/content/docs/en/examples/world-integrations/block-as-button.mdx new file mode 100644 index 0000000..f7e170f --- /dev/null +++ b/src/content/docs/en/examples/world-integrations/block-as-button.mdx @@ -0,0 +1,57 @@ +--- +title: Block as Button +description: Right-click any block of a chosen material to open a menu. The clickBlockType activator + block-context placeholders. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickBlockType` listens for right-clicks on any block of a given material, anywhere in the world. Different from `clickBlock` (which targets a specific block by coordinates). The opened menu can read the clicked block's position, world, type via `%activator_block_*%` placeholders. + + + +## What it teaches + +- The `clickBlockType` activator targeting a material +- Reading the click context via `%activator_block_x%`, `%activator_block_y%`, `%activator_block_z%`, `%activator_block_world%` +- Putting placeholders in the menu `title:` for dynamic titles per-click + +## Activator format + +```hocon +activators { + clickBlockType: ["DIAMOND_BLOCK"] +} +``` + +A list of materials. The menu opens when the player right-clicks any block whose type appears in the list. To add more triggers: + +```hocon +activators { + clickBlockType: ["DIAMOND_BLOCK", "EMERALD_BLOCK", "BEACON"] +} +``` + +Note this fires on EVERY block of the listed types - server-wide. For "specific block at known coordinates" (e.g., one signpost at the spawn area), use `clickBlock` instead with the world + coords. + +## Block context placeholders + +Inside the menu, `%activator_block_*%` exposes the clicked block: + +| Placeholder | Returns | +|---|---| +| `%activator_block_world%` | World name | +| `%activator_block_x%` | X coordinate | +| `%activator_block_y%` | Y coordinate | +| `%activator_block_z%` | Z coordinate | +| `%activator_block_type%` | Material name | + +The example uses these in the menu title - clicking different diamond blocks opens menus titled with each block's coords. Useful for "info about this specific signpost" UIs. + +## Try it + +1. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Place a diamond block somewhere in the world. +4. Right-click it. The menu opens with the block's coordinates in the title. diff --git a/src/content/docs/en/examples/world-integrations/index.mdx b/src/content/docs/en/examples/world-integrations/index.mdx new file mode 100644 index 0000000..5039959 --- /dev/null +++ b/src/content/docs/en/examples/world-integrations/index.mdx @@ -0,0 +1,18 @@ +--- +title: World Integrations +description: Region-triggered menus, NPC-click info menus, and block-as-button activators. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Region-triggered menus, NPC-click info menus, and block-as-button activators. + +## Menus in this category + + + +## Download + +Use the [Pack Builder](../builder/) to bundle these together with any other examples into a single zip. diff --git a/src/content/docs/en/examples/world-integrations/npc-click-info.mdx b/src/content/docs/en/examples/world-integrations/npc-click-info.mdx new file mode 100644 index 0000000..7f8724b --- /dev/null +++ b/src/content/docs/en/examples/world-integrations/npc-click-info.mdx @@ -0,0 +1,58 @@ +--- +title: NPC Click Info +description: A generic info-and-actions menu opened by clicking any of several Citizens NPCs. Reads NPC context to personalize the menu. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Where [NPC Vendor](../shops/npc-vendor) is one specific NPC opening one specific shop, this menu is a generic "what's this NPC about" UI that any of several NPCs can trigger. The menu reads the clicked NPC's id/name/world via context placeholders and offers actions that work for any of them. + + + +## What it teaches + +- Listing multiple NPC IDs in `clickNPC: [1, 2, 3]` so any of them opens this menu +- Personalizing the menu via `%activator_npc_name%`, `%activator_npc_id%`, `%activator_npc_world%` in title, item names, and lore +- Using `skullOwner: "%activator_npc_name%"` so the header item's player-head texture matches the clicked NPC's name +- Routing different NPCs to different downstream menus (left as an exercise via the openMenu action) + +## NPC routing pattern + +The example's "Talk to vendor" tile opens `ame_shop` for any NPC. A real server might route differently per NPC: + +```hocon +click { + actions { + rules { + and { + # Hypothetical: only NPC #2 opens the rare shop + if: "%activator_npc_id% == 2" + } + actions { openMenu: "ame_rare_shop" } + denyActions { openMenu: "ame_shop" } + } + } +} +``` + +For more than 2 routes, use `oneof`: + +```hocon +oneof: [ + { if: "%activator_npc_id% == 1", actions { openMenu: "ame_blacksmith_shop" } } + { if: "%activator_npc_id% == 2", actions { openMenu: "ame_alchemy_shop" } } + { if: "%activator_npc_id% == 3", actions { openMenu: "ame_armorer_shop" } } +] +``` + +`oneof` short-circuits on the first match, so each NPC ID hits exactly one branch. + +## Try it + +1. Install Citizens. +2. Spawn three NPCs, note their IDs. +3. Drop the bundle into `plugins/AbstractMenus/menus/example/`. Update `clickNPC: [1, 2, 3]` to your actual NPC IDs. +4. `/am reload`. +5. Right-click any of the NPCs. The menu opens with that NPC's data in the header. diff --git a/src/content/docs/en/examples/world-integrations/region-welcome.mdx b/src/content/docs/en/examples/world-integrations/region-welcome.mdx new file mode 100644 index 0000000..df5f689 --- /dev/null +++ b/src/content/docs/en/examples/world-integrations/region-welcome.mdx @@ -0,0 +1,61 @@ +--- +title: Region Welcome +description: Open a welcome menu automatically when a player enters a WorldGuard region. No command, the activator is the region itself. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +A common server pattern: when a player walks into spawn, show a welcome screen with quick-access buttons (rules, hub, kit). The activator here is `regionJoin` from WorldGuard, not a command - the menu opens itself when the player crosses the region boundary. + +## What it teaches + +- The `regionJoin` activator (and its sibling `regionLeave`) +- WorldGuard integration via region names +- Combining a passive welcome with quick-action tiles that link to other menus + + + +## How it works + +The activator block names one or more region IDs: + +```hocon +activators { + regionJoin: ["spawn"] +} +``` + +When the player enters the `spawn` region (defined in WorldGuard), the menu opens for them. Multiple region IDs can be listed; the menu opens whenever the player enters any of them. + +The menu has no money rules, no buy actions - it's a glorified bulletin board. The three quick-link tiles in the middle row each call `openMenu` to navigate the player to another example. + +## Region setup on the server + +This example assumes a WorldGuard region named `spawn` exists. Define it with: + +```text +/rg define spawn +``` + +Then put this menu in `plugins/AbstractMenus/menus/example/world-integrations/01-region-welcome/menu.conf`, and `/am reload`. Walking into the region triggers the menu. + +## Related activators + +- **`regionLeave`** - same shape, fires when the player leaves the region. Useful for "say goodbye" or "warn before PvP" use cases. +- **`clickEntity` / `clickNPC` / `clickBlock`** - other "the world triggers the menu" activators. See [Activators](/docs/en/general/activators/) for the full list. + +## Try it + +After installing the example pack: + +1. Install [WorldGuard](https://dev.bukkit.org/projects/worldguard). +2. Create a region named `spawn` with `/rg define spawn`. +3. Drop the bundle into `plugins/AbstractMenus/menus/example/`. +4. `/am reload`. +5. Walk into the region. diff --git a/src/content/docs/en/general/examples.mdx b/src/content/docs/en/general/examples.mdx index f0ebf4c..6234654 100644 --- a/src/content/docs/en/general/examples.mdx +++ b/src/content/docs/en/general/examples.mdx @@ -1,153 +1,23 @@ --- title: Examples -description: Reference menus you can copy-paste, with full HOCON inlined. +description: A catalog of ready-to-use AbstractMenus menu examples - copy, drop in, customize. --- -import { Aside, Code } from "@astrojs/starlight/components"; +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; -import statsConf from "../../../../examples/simple/stats.conf?raw"; -import serversConf from "../../../../examples/simple/servers.conf?raw"; -import cakeShopConf from "../../../../examples/simple/cake_shop.conf?raw"; -import kitsConf from "../../../../examples/medium/kits.conf?raw"; -import craftConf from "../../../../examples/medium/craft.conf?raw"; -import shopConf from "../../../../examples/medium/shop.conf?raw"; -import enhancerConf from "../../../../examples/advanced/enhancer.conf?raw"; -import enchanterConf from "../../../../examples/advanced/enchanter.conf?raw"; +The example catalog has moved. There's now a dedicated **Examples** section in the sidebar with ~14 menus organized by use case, each with its own page, syntax-highlighted code, badges for features and dependencies, plus single-file and zip-bundle download buttons. -
Menu author
+Start at the [Examples landing page](/docs/en/examples/), or jump straight to a category: -Sorted by complexity. Each menu's full HOCON is right here — copy the code into a file under `plugins/AbstractMenus/menus/` and run `/am reload`. - - - -## Simple - -### Player stats - -Reads vanilla statistics through PlaceholderAPI and renders four head items. - -**Requires:** PlaceholderAPI with the Player and Statistic expansions installed. - -![preview-stats](/docs/img/examples/stats.gif) - -
-stats.conf - -
- -### BungeeCord servers selector - -Lobby-style server picker. Shows online status and player count for each backend. - -**Requires:** BungeeCord, PlaceholderAPI with Player and Bungee expansions. - -![preview-servers](/docs/img/examples/servers.gif) - -
-servers.conf - -
- -### Cake shop - -Buy a cake for in-game currency. - -**Requires:** Vault plus an economy plugin. - -![preview-cakes](/docs/img/examples/cakes.gif) - -
-cake_shop.conf - -
- -## Medium - -### Daily kits - -Permission-gated kits with cooldown tracking via variables. - -**Requires:** LuckPerms. - -![preview-kits](/docs/img/examples/kits.gif) - -
-kits.conf - -
- -### Crafting - -Custom drag-and-drop crafting grid driven by `placedItem` rules. - -**Requires:** Nothing. - -![preview-craft](/docs/img/examples/craft.gif) - -
-craft.conf - -
- -### Shop - -Multi-category paginated shop with bindings, lore variants, and discount logic. - -**Requires:** Vault plus an economy plugin. - -![preview-shop](/docs/img/examples/shop.gif) - -
-shop.conf - -
- -## Advanced - -### Casino - -Slot-machine-style chance menu with animation frames and rewards. - -**Requires:** Vault plus an economy plugin. - -![preview-casino](/docs/img/examples/casino.gif) - -The casino spans multiple files. Source: [advanced/casino on GitHub](https://github.com/AbstractMenus/examples/tree/main/advanced/casino). - -### Admin panel - -Punish menu (kick / ban / mute) over an online-player catalog. - -**Requires:** Essentials (or any plugin that exposes those commands). - -![preview-admin](/docs/img/examples/padmin.gif) - -Multi-file. Source: [advanced/admin on GitHub](https://github.com/AbstractMenus/examples/tree/main/advanced/admin). - -### Item enhancer - -Click-driven item upgrade flow with variable-tracked level state. - -**Requires:** Nothing. - -![preview-enhancer](/docs/img/examples/enhancer.gif) - -
-enhancer.conf - -
- -### Random enchanter - -Roll a random enchantment from a weighted table. - -**Requires:** Nothing. - -![preview-enchant](/docs/img/examples/enchant.gif) - -
-enchanter.conf - -
+ + + + + + + + + + + + diff --git a/src/content/docs/ru/examples/admin-tools/index.mdx b/src/content/docs/ru/examples/admin-tools/index.mdx new file mode 100644 index 0000000..48053e4 --- /dev/null +++ b/src/content/docs/ru/examples/admin-tools/index.mdx @@ -0,0 +1,18 @@ +--- +title: Админ-инструменты +description: Браузер онлайн-игроков и быстрые модераторские меню под permission. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Браузер онлайн-игроков и быстрые меню для модераторов, защищённые permission-rule на уровне всего меню. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/admin-tools/online-players.mdx b/src/content/docs/ru/examples/admin-tools/online-players.mdx new file mode 100644 index 0000000..81796c1 --- /dev/null +++ b/src/content/docs/ru/examples/admin-tools/online-players.mdx @@ -0,0 +1,99 @@ +--- +title: Онлайн-игроки +description: Сгенерированное меню над каталогом PLAYERS. Список онлайн-игроков головами, ЛКМ - телепорт, Shift+ЛКМ - кик. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Главный сценарий для `GeneratedMenu`: постраничный браузер онлайн-игроков. Каждая голова собирается автоматически из каталога `PLAYERS`, в lore - здоровье и мир. ЛКМ телепортирует админа к игроку, Shift+ЛКМ кикает. Доступ ограничен админами через общий сниппет `rulesAdmin`. + +## Чему учит пример + +- Сборка генерируемого меню из каталога + матрицы + шаблонов +- Использование плейсхолдеров каталога (`%ctg_player_name%`, `%ctg_player_health%`, `%ctg_player_world%`) +- Различение типов кликов: `click { left { ... } shift_left { ... } }` +- Действие `command` с `console:` для запуска команд от имени консоли +- Встроенная пагинация через `pageNext` / `pagePrev` (общие `${buttonNext}` и `${buttonPrev}`) +- Плейсхолдеры пагинации: `%page%`, `%pages_count%`, `%pages_total%` +- Ограничение доступа на уровне меню: верхнеуровневое `rules: ${rulesAdmin}` + + + +## Как устроен GeneratedMenu + +Генерируемое меню собирается из трёх блоков: + +```hocon +catalog { type: PLAYERS } # источник динамического содержимого + +matrix { # как разложить элементы + cells: [ "_________", "_xxxxxxx_", ... ] + templates { + "x" { ... шаблон предмета ... } + } +} + +items: [ # статичные предметы (рамки, кнопки, счётчик) + ... +] +``` + +Каталог выдаёт поток объектов. Матрица решает, куда какой объект каталога попадёт (ячейки `x` поочерёдно занимают записи из каталога). Статичные предметы из `items:` рендерятся на каждой странице на одних и тех же местах. + +## Плейсхолдеры каталога + +Внутри шаблонов матрицы текущий объект каталога доступен через `%ctg_%`. У каталога `PLAYERS` объект игрока имеет ключи `name`, `health`, `food_level`, `world`, `gamemode` и ещё несколько. У разных типов каталогов - разные ключи, полную таблицу смотрите в [документации по генерации](/docs/ru/advanced/generation/). + +## Типы кликов + +Блок `click` позволяет задать обработчики на конкретный тип нажатия: + +- **`left { ... }`** - только ЛКМ +- **`right { ... }`** - только ПКМ +- **`shift_left { ... }`** - Shift + ЛКМ +- **`shift_right { ... }`** - Shift + ПКМ +- **`drop { ... }`** - клавиша дропа (Q) + +Если описан только один вариант клика, остальные клики ничего не делают. В этом меню `left` отвечает за телепорт, а `shift_left` - за кик. Так обычный shift+ЛКМ не приведёт случайно к телепорту. + +## Пагинация + +Генерируемые меню автоматически разбиваются на страницы, если в каталоге больше записей, чем влезает в матрицу. Действия `pageNext` и `pagePrev` переключают страницы. Общие шаблоны `${buttonNext}` и `${buttonPrev}` уже навешивают эти действия на стрелки. + +Если меню умещается на одну страницу (записей в каталоге меньше, чем слотов в матрице), кнопки next/prev всё равно отрисуются, но их действия станут no-op. + +Плейсхолдеры `%page%`, `%pages_count%` и `%pages_total%` показывают состояние пагинации - удобно для заголовков вида "Страница 2 / 5" или счётчика в футере. + +## Кастомизация + +Чтобы оставить только часть игроков, добавьте фильтры в каталог: + +```hocon +catalog { + type: PLAYERS + filters { + permission: "myserver.online_visible" + notWorld: ["staff_world"] + } +} +``` + +Чтобы добавить ещё админских действий, расширьте блок `click` хендлерами `right`, `shift_right` и т.д. - каждый со своей командой через действие `command`. + +Чтобы полностью поменять источник данных (просматривать миры, регионы, BungeeCord-серверы), замените `catalog { type: ... }` на `WORLDS`, `BUNGEE_SERVERS` или любой другой зарегистрированный тип каталога. + +## Попробовать + +После установки набора примеров: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Убедитесь, что у вашей учётки есть право `abstractmenus.admin`. +4. Введите `/ame_online` в игре. +5. ЛКМ по голове игрока - телепорт. Shift+ЛКМ - кик. diff --git a/src/content/docs/ru/examples/admin-tools/quick-mod.mdx b/src/content/docs/ru/examples/admin-tools/quick-mod.mdx new file mode 100644 index 0000000..69c4796 --- /dev/null +++ b/src/content/docs/ru/examples/admin-tools/quick-mod.mdx @@ -0,0 +1,76 @@ +--- +title: Quick Mod +description: Меню быстрых действий для админов - переключение режима игры, broadcast, браузер онлайн-игроков, телепорт на спавн. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Меню-инструментарий для админов. Само меню закрыто на верхнем уровне через `rules: ${rulesAdmin}` - не-админам, которые ввели `/ame_mod`, оно вообще не откроется (не нужно навешивать правила на каждый клик отдельно). + + + +## Чему учит пример + +- **Правила на уровне меню**: верхнеуровневый `rules: ...` блокирует открытие, а не просто прячет предметы +- Действие `setGamemode` со стандартными именами режимов +- Действие `broadcast` для сообщений на весь сервер +- Действие `command` с `player:` (от имени игрока) против `console:` (от имени консоли) +- Цепочка хабов: это админ-меню открывает [Онлайн-игроков](./online-players) для более точечной модерации + +## Правила на меню vs на предмете + +Закрыть доступ можно двумя способами: + +**На уровне меню** (этот пример): + +```hocon +rules: ${rulesAdmin} +``` + +В начале файла. Если правило не проходит при срабатывании активатора, меню вообще не откроется. + +**На уровне предмета**: + +```hocon +{ + slot: 4 + ... + click { + rules { permission: "..." } + actions { ... } + } +} +``` + +Внутри блока `click` у конкретного предмета. Меню открывается всем, но отдельные предметы под правилами. + +Для админ-инструментария правильный выбор - на уровне меню: не-админам не нужно даже видеть раскладку. Для магазина с VIP-предметами правильный выбор - на уровне предмета: видеть ассортимент должны все. + +## Действие command + +`command` запускает серверные команды через обработчик действий: + +```hocon +command { + player: "spawn" # запускает /spawn от имени игрока +} +``` + +Или от консоли: + +```hocon +command { + console: "kick %ctg_player_name%" # запускает /kick ... от имени консоли +} +``` + +Форма `player:` уважает права игрока - у него должно быть разрешение на эту команду. Форма `console:` обходит проверки прав (используйте аккуратно). Для админских команд вроде /ban лучше брать `console:` из меню, уже закрытого по правам, чтобы не перепроверять авторизацию дважды. + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Выдайте себе `abstractmenus.admin` (общий шаблон `rulesAdmin` ссылается на этот узел). +4. Введите `/ame_mod` в игре. diff --git a/src/content/docs/ru/examples/builder.mdx b/src/content/docs/ru/examples/builder.mdx new file mode 100644 index 0000000..fb19443 --- /dev/null +++ b/src/content/docs/ru/examples/builder.mdx @@ -0,0 +1,44 @@ +--- +title: Pack Builder +description: Выбери нужные меню галочками и собери кастомный zip-бандл. Комбинируешь по категориям, получаешь одну загрузку со всем что нужно. +--- + +import PackBuilder from '@components/PackBuilder.astro'; + +Скачай нужные меню одним zip-ом сразу со страницы, а не прокликивая страницу каждого по отдельности. Поставь галочки на нужных, жми "Собрать бандл" - получаешь один архив с готовой структурой папок, который можно сразу распаковать на сервер. + + + +## Как пользоваться + +1. Поставь галочки на нужных меню. +2. Чекбокс рядом с категорией - выбрать всё в категории сразу. +3. Кнопки "Select all" / "Select none" вверху - применяют ко всему. +4. Кликни "Build pack (.zip)". +5. Распакуй zip в `plugins/AbstractMenus/menus/example/` на сервере. +6. `/am reload`. +7. Запусти любую из включённых команд (имя команды показано в каждом меню). + +Структура zip совпадает со структурой вики, так что распаковка прямо в `plugins/AbstractMenus/menus/example/` сразу даёт рабочий набор. + +## Что внутри бандла + +На каждое выбранное меню: + +- `//menu.conf` - сам конфиг меню +- `//templates.conf` - если у меню есть локальные шаблоны + +И один раз на весь бандл (только если хоть одно меню это использует): + +- `_shared/templates.conf` - общие пресеты звуков, deny, бордеры, кнопки, rule-шорткаты + +Плюс: + +- `INSTALL.txt` - инструкция по установке с датой генерации + +## Советы + +- **Нужны только сниппеты**: категория snippets - маленькие демо одной фичи каждое. Полезно для обучения. +- **Стартовый набор для сервера**: возьми хаб, магазин, киты, info-pages и одно меню казино. ~10 меню покрывают базовый survival. +- **Просто потыкать**: начни со сниппетов и foundation-меню (cake-shop, main-hub, basic-kit, rules) чтобы прочувствовать. +- **Бандл идентичный по структуре независимо от языка**: расклад одинаковый, отличается только in-game текст. Хочешь EN-строки - переключи на `/docs/en/examples/builder/`. diff --git a/src/content/docs/ru/examples/casino-and-games/daily-lottery.mdx b/src/content/docs/ru/examples/casino-and-games/daily-lottery.mdx new file mode 100644 index 0000000..3bbca64 --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/daily-lottery.mdx @@ -0,0 +1,72 @@ +--- +title: Ежедневная лотерея +description: Лотерея с джекпотом, использующая глобальную переменную как призовой фонд. Каждый билет либо забирает всё, либо пополняет банк. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Самое заметное меню в категории казино с точки зрения сервера - лотерея с реальным растущим джекпотом. Каждый билет за $100 либо забирает весь банк с шансом 1 к 50, либо отдаёт в него $50. Джекпот копится по всем игрокам и сессиям, пока кто-нибудь не выиграет. + + + +## Что показывает + +- Глобальную переменную как постоянный накопитель приза +- Действие `incVar` с явной величиной (а не просто +1) +- Чтение глобальной переменной внутри другого действия: `giveMoney: "%var_:ame_lottery_jackpot:0%"` +- Связку `bulk` с inline-блоками `rules { chance: ... } actions { ... } denyActions { ... }` для ветвления +- Серверное объявление через `broadcast` + +## Как работает ветка выигрыш/проигрыш + +Внутри `actions { }` у клика мы используем `bulk`, чтобы обернуть одну условную группу под-действий: + +```hocon +bulk: [ + { + rules { chance: 2 } + actions { + # Ветка победы + giveMoney: "%var_:ame_lottery_jackpot:0%" + setVar: "ame_lottery_jackpot::0" + ... + } + denyActions { + # Ветка проигрыша + incVar { name: "ame_lottery_jackpot", amount: 50 } + ... + } + } +] +``` + +`rules { chance: 2 }` бросает 2% вероятность. Если выпало - выполняется `actions` (выплата + сброс джекпота). Если нет - `denyActions` (плюс $50 в джекпот). + +Обёртка `bulk` нужна, чтобы вся условная конструкция была одной записью в списке - так можно потом добавить ещё условные исходы (10% шанс на половину джекпота и т.п.), просто дописав в список bulk. + +## Чтение переменных в аргументах действий + +`giveMoney: "%var_:ame_lottery_jackpot:0%"` - плейсхолдер раскрывается в текущее значение джекпота (например, `5000`), и `giveMoney` получает это число. В связке с `setVar: "ame_lottery_jackpot::0"` сразу после получается паттерн "выплати, потом сбрось". + +Порядок важен: выплата должна произойти до сброса, иначе игрок получит $0. + +## Подводные камни + +Это самостоятельное демо. Реальные лотерейные системы обычно хотят: + +- **Сохранение**: чтобы переменная джекпота переживала перезапуски сервера (за это отвечает хранилище переменных AbstractMenus, если в конфиге `syncVariables: true`) +- **Лимиты в день**: временная переменная per-player (`setVarp ::1d`), чтобы ограничить покупки билетов +- **Сброс/аудит для админа**: в этом меню есть базовый сброс; в проде нужны логированные транзакции +- **Антифрод**: rate limiting + дебаунс кликов уже закрывает плагин своими per-menu кулдаунами + +В вашей реальной реализации наслаивайте всё это поверх показанного паттерна. + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give <вы> 5000`. +4. Введите `/ame_lottery` в игре. +5. Купите 20-30 билетов. Джекпот растёт. В какой-то момент один из них выиграет. diff --git a/src/content/docs/ru/examples/casino-and-games/enchanter.mdx b/src/content/docs/ru/examples/casino-and-games/enchanter.mdx new file mode 100644 index 0000000..073103e --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/enchanter.mdx @@ -0,0 +1,78 @@ +--- +title: Случайный зачарователь +description: Лотерея зачарований через drag-and-drop. Кладёте инструмент, кликаете "Зачаровать!", получаете обратно случайное зачарование. Показывает draggable-слоты, placeItem, правила placedItem и randActions. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Самое насыщенное меню в паке. Сочетает drag-and-drop слоты (игрок физически кладёт предмет в меню), правило `placedItem` (проверить, что в слоте), действие `placeItem` (записать в слот), `removePlaced` (очистить слот), `setButton` (заменить статичный предмет) и `randActions` (выбрать один исход случайно). Всё вместе даёт игроку опыт "брось предмет, разыграй зачарование". + +## Что показывает + +- Поле `draggable:` со строкой-матрицей, описывающей, какие слоты принимают drag-and-drop +- Правило `placedItem` для проверки содержимого draggable-слота +- Инвертированные правила с префиксом `-`: `-placedItem { ... }` означает "НЕ положено" +- Действие `placeItem` для записи предмета в draggable-слот +- `removePlaced` для очистки draggable-слота +- `setButton` для динамической перезаписи обычного предмета меню +- Семейство плейсхолдеров `%changed_item_*%`, фиксирующее то, что игрок перетащил +- Связку `randActions` с `placeItem` для случайных исходов + + + +## Как работает drag-and-drop + +Поле `draggable:` в корне меню принимает матрицу строк, по одной на ряд, где `x` помечает слот, в который игроки могут положить предмет. В этом примере drag-and-drop стоит на слоте 11 (вход) и слоте 15 (результат). + +```hocon +draggable: [ + "---------", + "--x---x--", + "---------" +] +``` + +Когда слот draggable, игрок может взять предметы из хотбара/инвентаря и положить в этот слот. Плагин отслеживает их содержимое отдельно от items меню - drag-and-drop слоты это отдельный слой поверх сетки меню. + +Внутри actions можно: + +- **Правило `placedItem`** - проверить, что в draggable-слоте. `placedItem { slot: 11, material: AIR }` истинно, когда слот 11 пуст. +- **`-placedItem`** - инвертированно, та же проверка с обратным результатом. +- **Действие `placeItem`** - положить предмет в draggable-слот. +- **Действие `removePlaced`** - очистить draggable-слот. + +## Как работает случайное зачарование + +Клик по кнопке зачарования делает три шага: + +1. **Проверка правила**: `-placedItem { slot: 11, material: AIR }` - "слот 11 НЕ пуст". Если игрок ничего не положил, идём в denyActions. +2. **`randActions`**: случайно выбирает запись из списка. Каждая запись вызывает `placeItem`, чтобы положить копию предмета игрока (сохранённого через `%changed_item_serialized%`) в слот 15 с одним из четырёх наборов зачарований. +3. **`removePlaced: 11`**: очищает входной слот. Оригинальный предмет уходит, зачарованная копия лежит в слоте 15. + +Ветка deny (когда вход пуст) проигрывает звук и через `setButton: ${resultStub}` сбрасывает заглушку слота результата обратно в серое стекло. + +## Кастомизация + +Чтобы добавить ещё один исход зачарования, допишите в список `randActions`: + +```hocon +{ placeItem: ${placeItemBase} { enchantments { mending: 1 } } } +``` + +По умолчанию у каждого исхода одинаковый вес. Для взвешенного рандома повторяйте исходы (запись, встречающаяся дважды, имеет вдвое больший шанс). + +## Попробовать + +После установки example pack: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_enchanter` в игре. +4. Перетащите меч/кирку в левый слот. +5. Кликните "Зачаровать!". В правом слоте появится результат со случайным зачарованием. diff --git a/src/content/docs/ru/examples/casino-and-games/index.mdx b/src/content/docs/ru/examples/casino-and-games/index.mdx new file mode 100644 index 0000000..4ed0119 --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/index.mdx @@ -0,0 +1,18 @@ +--- +title: Казино и игры +description: Анимированные рулетки, слоты, lucky-chest, ежедневная лотерея с глобальным джекпотом. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Анимированные рулетки, слот-машины, lucky-chest и ежедневная лотерея с глобальной переменной как джекпот. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/casino-and-games/lucky-chest.mdx b/src/content/docs/ru/examples/casino-and-games/lucky-chest.mdx new file mode 100644 index 0000000..a2d9512 --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/lucky-chest.mdx @@ -0,0 +1,55 @@ +--- +title: Счастливый сундук +description: Платите $50, открываете сундук, получаете один из 4 уровней наград. Показывает взвешенные случайные исходы через повторение записей в randActions. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Лотерейный сундук с оплатой за открытие. Каждый клик стоит $50 и разыгрывает один из четырёх уровней приза (обычный / необычный / редкий / мифический) со взвешенной вероятностью. Сделано на `randActions` и паттерне "повторяй записи, чтобы добавить вес". + + + +## Что показывает + +- Взвешенные случайные исходы через повторение записей в `randActions` +- Связку правила money, действия takeMoney и randActions в одном click +- Многоуровневую таблицу лута без написания кода на Java + +## Взвешенный рандом через повторение + +`randActions` равномерно выбирает одну запись. Чтобы какие-то исходы были вероятнее, просто включайте их несколько раз. С 12 записями: + +- **6 записей** обычной награды = 6/12 = 50% (в примере целились в ~60%) +- **3 записи** необычной = 3/12 = 25% +- **1 запись** редкой = 1/12 = ~8% +- **2 записи** мифической = 2/12 = ~17% (целились в 2%, см. примечание) + +Список выше многословный - 12 строк на 4 типа приза. Зато он читается куда лучше, чем JS-выражение со взвешенным рандомом, и в нём почти нельзя ошибиться (нет шанса опечататься в весе). + +Для более тонких вероятностей (например, 1.5%) используйте список из 200+ записей. Или вынесите приз в отдельный шаблон и ссылайтесь на него много раз, чтобы список оставался компактным: + +```hocon +prizeCommon { itemAdd { material: COOKED_BEEF, count: 12 }, sound: ... } +prizeRare { itemAdd { material: ENCHANTED_GOLDEN_APPLE }, sound: ... } + +randActions: [ + ${prizeCommon} + ${prizeCommon} + ${prizeCommon} + ${prizeRare} +] +``` + +## Про точность весов + +Вероятности, которые написаны в lore меню (60/30/8/2), не точно совпадают с реальными весами в списке - демо приближённое, чтобы список был короче. Чтобы получить ровно 60/30/8/2, нужно 50 записей (30/15/4/1). Подгоняйте под себя. + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give <вы> 1000`. +4. Введите `/ame_chest` в игре. +5. Кликните "Открыть сундук". Повторите, чтобы увидеть разные уровни. diff --git a/src/content/docs/ru/examples/casino-and-games/roulette.mdx b/src/content/docs/ru/examples/casino-and-games/roulette.mdx new file mode 100644 index 0000000..4e17bd4 --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/roulette.mdx @@ -0,0 +1,88 @@ +--- +title: Рулетка +description: Анимированная рулетка из 8 кадров, замедляющаяся к концу, плюс случайный приз через randActions после остановки. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Открываете меню, и иконка предмета быстро перебирает алмаз -> изумруд -> золото -> алмаз..., постепенно замедляясь. Когда всё останавливается, игрок получает случайную награду (или не получает ничего). Показывает формат AnimatedMenu с `frames`, эффект замедления через рост `delay` и хук `onAnimEnd` для логики "что происходит после анимации". + +## Что показывает + +- Блок `frames` как альтернатива `items` для анимированных меню +- `delay:` (в тиках) на каждом кадре и `clear: false` для сохранения статичных предметов +- Совместная работа статичного блока `items:` и `frames` +- Хук `onAnimEnd` для запуска действий по завершении анимации +- `randActions` для выбора одного из N исходов случайно +- Темп анимации - быстро в начале, медленно к концу + + + +## Как работает AnimatedMenu + +Если в файле меню в корне есть блок `frames:`, плагин считает меню анимированным. `frames` заменяет обычный однопроходный рендеринг `items` на последовательность кадров, проигрываемых по порядку. + +У каждого кадра есть: + +- **`delay:`** задержка перед этим кадром в тиках (по умолчанию 20 = 1 секунда) +- **`clear:`** очищать ли инвентарь перед рендером items этого кадра (по умолчанию `true`) +- **`items:`** предметы, которые добавляются в этом кадре +- Опционально: `rules`, `onStart`, `onEnd` + +Статичный блок `items:` в корне работает вместе с кадрами. Если у кадров `clear: false`, эти статичные предметы сохраняются - именно то, что нужно для рамок и кнопки закрытия. + +## Трюк с замедлением + +Рулетка ощущается правильно, когда замедляется к концу. Делаем это через постепенно растущие значения `delay:`: + +```hocon +frames: [ + { delay: 2, ... } # 0.1s - очень быстро + { delay: 2, ... } + { delay: 2, ... } + { delay: 2, ... } + { delay: 4, ... } # начинает замедляться + { delay: 6, ... } + { delay: 10, ... } + { delay: 20, ... } # 1s - драматичная пауза на финальном кадре +] +``` + +Всего: примерно 2.4 секунды. Финальный кадр с NETHER_STAR держится целую секунду, пока `onAnimEnd` выдаёт приз. + +## Выдача приза + +`onAnimEnd` запускается после того, как закончится последний кадр. Внутри блок `randActions`, который случайно выбирает один из четырёх исходов: + +```hocon +onAnimEnd { + randActions: [ + { sound: ${successSound}, itemAdd { material: DIAMOND }, message: "..." } + { sound: ${successSound}, itemAdd { material: EMERALD, count: 2 }, message: "..." } + ... + { sound: ${failSound}, message: "&7В следующий раз повезёт." } + ] +} +``` + +У каждой записи свой блок действий. Один выбирается равномерно случайно. Исход "не выиграл" - просто звук и сообщение, без `itemAdd`. + +## Кастомизация + +Чтобы взвешивать исходы по-разному, повторяйте их. Запись, встречающаяся дважды, имеет вдвое больший шанс. Чтобы изменить продолжительность вращения, правьте значения `delay:` - общее время равно сумме всех задержек. + +Чтобы сменить, какие предметы появляются во время вращения, замените `material:` в items каждого кадра. Они декоративные - реальный приз идёт из `onAnimEnd`, а не "куда попадёт вращение". + +## Попробовать + +После установки example pack: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_roulette` в игре. Смотрите на вращение, узнайте, что выпало. diff --git a/src/content/docs/ru/examples/casino-and-games/slot-machine.mdx b/src/content/docs/ru/examples/casino-and-games/slot-machine.mdx new file mode 100644 index 0000000..e43b98a --- /dev/null +++ b/src/content/docs/ru/examples/casino-and-games/slot-machine.mdx @@ -0,0 +1,39 @@ +--- +title: Слот-машина +description: Три барабана, крутящихся вместе. Анимация и взвешенное распределение случайных исходов. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Слот-машина с тремя барабанами и четырьмя уровнями исходов (мимо / вишни / колокольчики / джекпот). Ближе к настоящей слот-машине по UX, чем [Рулетка](./roulette) - больше барабанов, больше визуала, более выраженное замедление. Те же кирпичики: `frames` + `onAnimEnd` + `randActions`. + + + +## Что показывает + +- Анимацию по нескольким слотам: каждый кадр обновляет 3 барабана сразу +- Взвешенные таблицы призов (3/1/1/1 в списке randActions примерно равно 60/20/20/что-то) +- Связку визуальной анимации (финальный кадр показывает три звезды) и реальной выдачи приза (она независима, в randActions) + +## Барабаны синхронно + +В items каждого кадра три предмета - по одному на барабан. Все обновляются на одном переходе кадра, поэтому визуально все три барабана крутятся вместе, а не независимо. + +Чтобы реально крутить барабаны независимо, понадобились бы три отдельных анимированных меню друг поверх друга или код в расширении, который ведёт каждый барабан по своему таймеру. Для демо хватает синхронных барабанов - игрок получает ощущение слот-машины без инженерного оверхеда. + +## Развязка анимации и исхода + +Визуальные кадры показывают разные комбинации символов. Реальный приз решается в `randActions` внутри `onAnimEnd`, полностью независимо от символов на последнем кадре. + +Это сделано намеренно. Если бы приз был привязан к визуальному исходу (например, "ты увидел три вишни - вот тебе вишнёвый приз"), пришлось бы контролировать, на каком символе остановится анимация. Это требует динамических кадров на каждый розыгрыш. Сильно сложнее. + +Развязанный подход - это то, что и делают настоящие слот-машины. Визуал - театр. Математика - в розыгрыше приза. + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give <вы> 500`. +4. Введите `/ame_slots` в игре. Смотрите на вращение. Читайте сообщение с призом. diff --git a/src/content/docs/ru/examples/cosmetics/color-skins.mdx b/src/content/docs/ru/examples/cosmetics/color-skins.mdx new file mode 100644 index 0000000..4113ce1 --- /dev/null +++ b/src/content/docs/ru/examples/cosmetics/color-skins.mdx @@ -0,0 +1,65 @@ +--- +title: Цветные скины +description: Выбор скина через setSkin от SkinsRestorer. Три предустановленных скина и кнопка сброса. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Действие `setSkin` меняет скин игрока через SkinsRestorer. В этом примере три готовых скина и кнопка сброса. Вызов `closeMenu` непосредственно перед `setSkin` обязателен: смена скина вызывает респаун игрока, и открытый инвентарь приведёт к крашу клиента. + +## Что показывает пример + +- Действие `setSkin` с параметрами `texture` + `signature` +- Сокращение `resetSkin: true` для возврата стандартного скина +- Почему `closeMenu` должен идти перед `setSkin` (респаун + открытый инвентарь = краш клиента) +- Использование `PLAYER_HEAD` с полем `texture:` для отображения превью скина в качестве предмета меню + + + +## Важно: порядок действий + +Внутри блока `actions { ... }` ключи выполняются в том порядке, в котором они идут в файле. Документация плагина прямо требует ставить `closeMenu` перед `setSkin`: + +```text +:::caution +Before this action, you need to set the closeMenu action, because when the +skin is changed, player respawning. If the menu is opened, it may cause a +critical error for the client. +::: +``` + +В каждом блоке click выше `closeMenu: 1` стоит первым, затем идёт `setSkin`. Не меняй порядок. + +## Где взять реальные texture и signature + +Заглушки в этом примере (`REPLACE_WITH_TEXTURE_BASE64_GOLD` и т.д.) сами по себе не сработают. Чтобы получить реальные значения: + +1. Зайди на [MineSkin.org](https://mineskin.org). +2. Загрузи изображение или выбери готовый скин. +3. Скопируй поле Texture Data в параметр `texture:`. +4. Скопируй поле Texture Signature в параметр `signature:`. + +Оба поля - длинные base64-строки. Поле `texture` у отображаемого `PLAYER_HEAD` (превью) - это короткий хеш текстуры (та часть, что идёт после `http://textures.minecraft.net/texture/`). + +## Кастомизация + +Чтобы добавить новый скин, скопируй любой блок с предметом, поменяй `slot:`, `name:`, превью `texture:` и содержимое `setSkin`. Общий шаблон со звуком `${successSound}` сохраняет одинаковую звуковую обратную связь для всех вариантов. + +Если превью не нужно (скажем, хочешь использовать баннер вместо головы), убери поле `texture:` и поменяй `material:` на `RED_BANNER` или что подходит к стилю. + +## Попробовать + +После установки набора примеров: + +1. Поставь [SkinsRestorer](https://www.spigotmc.org/resources/2124/). +2. Замени заглушки `REPLACE_WITH_*` на реальные texture/signature. +3. Положи бандл в `plugins/AbstractMenus/menus/example/`. +4. Выполни `/am reload`. +5. Введи `/ame_skins` в игре. +6. Кликни на скин. Перезайди, чтобы увидеть изменения. diff --git a/src/content/docs/ru/examples/cosmetics/index.mdx b/src/content/docs/ru/examples/cosmetics/index.mdx new file mode 100644 index 0000000..8c4ca11 --- /dev/null +++ b/src/content/docs/ru/examples/cosmetics/index.mdx @@ -0,0 +1,18 @@ +--- +title: Косметика +description: Селекторы тегов, префиксов и скинов - lpMetaSet, addGroup/removeGroup, шаблонная подстановка для вариантов. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Селекторы тегов, префиксов и скинов через `lpMetaSet`, `addGroup`/`removeGroup` и шаблонную подстановку для вариантов одного предмета. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/cosmetics/prefixes.mdx b/src/content/docs/ru/examples/cosmetics/prefixes.mdx new file mode 100644 index 0000000..cda31f1 --- /dev/null +++ b/src/content/docs/ru/examples/cosmetics/prefixes.mdx @@ -0,0 +1,68 @@ +--- +title: Префиксы +description: Выбор префикса через группы LuckPerms. Взаимная исключимость через removeGroup + addGroup в bulk-действии. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Другой подход в сравнении с примером [Теги](./tags). Вместо записи в meta это меню управляет членством в группах LuckPerms. Каждый префикс соответствует одной группе; при выборе префикса игрок удаляется из других групп префиксов и добавляется в выбранную. Взаимная исключимость встроена. + + + +## Что показывает пример + +- Действия `addGroup` / `removeGroup` для членства в группах LuckPerms +- Использование `bulk` для нескольких removeGroup + addGroup в одном клике +- Префиксы более высоких рангов закрыты пермишенами + +## Зачем bulk + +При каждом выборе префикса последовательно выполняются три действия: + +```hocon +bulk: [ + { removeGroup: "ame_prefix_member" } + { removeGroup: "ame_prefix_donor" } + { addGroup: "ame_prefix_admin" } +] +``` + +Нельзя написать `removeGroup: ...` дважды на верхнем уровне блока `actions {}` - ключи объекта в HOCON должны быть уникальными. `bulk` оборачивает последовательность действий, каждый элемент - своя группа действий. + +Этот паттерн (удалить все остальные, добавить эту) гарантирует, что игрок состоит ровно в одной группе префикса. Без удалений у игрока накапливается членство в группах и выходит несколько префиксов одновременно. + +## Теги vs Префиксы + +Два способа управлять префиксом в чате: + +- Теги (lpMetaSet) - гибкие, тонкая настройка, без иерархии групп. Подходят для чисто косметических символов. +- Префиксы (addGroup) - интегрируются с группами LuckPerms, наследуют права от группы, дружат с плагинами чата, читающими `%luckperms_prefix%`. Подходят, когда префикс должен ещё и давать дополнительные права. + +Большинство серверов использует второй вариант для цвета имени в чате и первый - для дополнительного декоративного тега. + +## Настройка + +Перед использованием меню создай группы LuckPerms: + +```text +/lp creategroup ame_prefix_member +/lp group ame_prefix_member meta addprefix 50 "&a[Member] " + +/lp creategroup ame_prefix_donor +/lp group ame_prefix_donor meta addprefix 100 "&6[Donor] " + +/lp creategroup ame_prefix_admin +/lp group ame_prefix_admin meta addprefix 1000 "&c[Admin] " +``` + +Числовой вес (`50`, `100`, `1000`) задаёт приоритет, когда у игрока несколько префиксов - больший выигрывает. Это меню всё равно держит ровно один префикс, поэтому вес тут особо не важен, но привычку выставлять его лучше иметь. + +## Попробовать + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. Создай три группы из примера выше. +3. Выполни `/am reload`. +4. Введи `/ame_prefixes` в игре. +5. Кликни на любой префикс. Проверь через `/lp user <ты> info` (одна из групп префикса будет в списке твоих групп). diff --git a/src/content/docs/ru/examples/cosmetics/tags.mdx b/src/content/docs/ru/examples/cosmetics/tags.mdx new file mode 100644 index 0000000..fbeff4a --- /dev/null +++ b/src/content/docs/ru/examples/cosmetics/tags.mdx @@ -0,0 +1,66 @@ +--- +title: Теги +description: Выбор тега, который записывает символ в LuckPerms meta игрока. Бесплатные теги для всех и тег-корона для VIP. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +На многих серверах рядом с ником в чате показывается тег - небольшой символ, который читается из ключа LuckPerms meta. Это меню позволяет игроку выбрать нужный тег: меню записывает выбранный символ в этот meta-ключ. Подключи плагин форматирования чата, который читает `%luckperms_meta_ame_tag%` (или другой плейсхолдер, который ждёт твой форматтер), и тег появится в чате. + + + +## Что показывает пример + +- Действие `lpMetaSet` со списком пар ключ/значение `metaList` +- Действие `lpMetaRemove` для очистки тега +- Совмещение бесплатных тегов и тегов под пермишеном в одном меню +- Паттерн с двумя ветками действий: `actions { ... } denyActions: ${denyNoPerm}` для VIP-тега + +## Как работает LuckPerms meta + +`lpMetaSet` записывает одно или несколько meta-значений в профиль игрока в LuckPerms: + +```hocon +lpMetaSet { + metaList: [ + { key: "ame_tag", value: "&c♥" } + ] +} +``` + +Имя meta-ключа (здесь `ame_tag`) выбираешь сам - бери что-то специфичное для своего сервера. Значение - это то, что попадёт в чат (в примере - цветной символ). За один вызов можно установить сразу несколько ключей: + +```hocon +metaList: [ + { key: "prefix", value: "&7[Member]" }, + { key: "ame_tag", value: "&c♥" }, + { key: "show_role", value: "true" } +] +``` + +`lpMetaRemove` принимает список ключей для удаления: + +```hocon +lpMetaRemove: ["ame_tag"] +``` + +Эти действия работают только если в качестве провайдера прав активен LuckPerms. Если активен другой провайдер, действие выведет предупреждение в лог и ничего не сделает - меню не сломается, просто тег не применится. + +## Показ тега в чате + +Установить meta - это половина дела. Форматтер чата должен его прочитать. Большинство плагинов чата (DeluxeChat, ChatFormatter и т.д.) принимают плейсхолдеры PlaceholderAPI вида `%luckperms_meta_%` в строке формата: + +```yaml +chatformat: "&8[%luckperms_meta_ame_tag%&8] &f%player_name%: %message%" +``` + +Добавь это в конфиг плагина чата, перезапусти - и тег появится рядом с ником. + +## Попробовать + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. Выполни `/am reload`. +3. Введи `/ame_tags` в игре. +4. Кликни на любой бесплатный тег - meta установится. Проверь через `/lp user <ты> meta info` (увидишь `ame_tag` в списке). diff --git a/src/content/docs/ru/examples/donate/index.mdx b/src/content/docs/ru/examples/donate/index.mdx new file mode 100644 index 0000000..1739e31 --- /dev/null +++ b/src/content/docs/ru/examples/donate/index.mdx @@ -0,0 +1,18 @@ +--- +title: Донат +description: Магазины ранков и предметов с дуальными HAS/NO_PERM вариантами. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Магазины ранков и донатных предметов с паттерном "у тебя есть рангX/у тебя нет рангX" через дуальные item-варианты. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/donate/item-store.mdx b/src/content/docs/ru/examples/donate/item-store.mdx new file mode 100644 index 0000000..5a20ada --- /dev/null +++ b/src/content/docs/ru/examples/donate/item-store.mdx @@ -0,0 +1,59 @@ +--- +title: Магазин предметов +description: Донат-магазин предметов. Зачарованные вещи, элитры, декоративные головы за донатные токены (или любую другую валюту). +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Дополнение к [Донат-рангам по уровням](./tiered-ranks). Там магазин продаёт права, здесь - предметы, которые игрок может использовать напрямую. Тот же паттерн money/takeMoney, что и в обычном магазине, только цены выше: подразумевается оплата донатной валютой, а не накопленными монетами. + + + +## Что показывает + +- Предметы с кастомными именами и предзачарованиями через `itemAdd { name: ..., enchantments { ... } }` +- Флаг `flags: HIDE_ATTRIBUTES` для инструментов и мечей (иначе ваниль покажет под именем диапазон урона) +- Цены в диапазоне донатной валюты + +## Как выбрать провайдера экономики + +По умолчанию `takeMoney: 1000` использует провайдера, который указан в `config.conf providers.economy`. Для системы донатных токенов обычно хочется направить это через отдельный провайдер (например, [аддон PlayerPoints](https://github.com/AbstractMenus/PlayerPointsAddon)), а не через обычную экономику Vault. + +Два варианта: + +**1. Донатная валюта на весь сервер:** в `config.conf` сделайте донатные токены экономикой по умолчанию. Тогда все вызовы `takeMoney` пойдут через них. Самый простой путь, если весь сервер живёт на токенах. + +**2. Провайдер на конкретное действие:** оставьте Vault по умолчанию, но в каждом действии этого меню укажите донатного провайдера явно: + +```hocon +takeMoney: { amount: 1000, provider: "playerpoints" } +``` + +Так обычный магазин продолжит работать с Vault, а донат-магазин будет списывать токены. + +## Кастомные предметы + +Мифический меч показывает, как собрать неванильный предмет через `itemAdd`: + +```hocon +itemAdd { + material: NETHERITE_SWORD + name: "&dМифический меч" + enchantments { + sharpness: 5 + unbreaking: 3 + mending: 1 + } +} +``` + +Любое поле, которое работает на отображаемом предмете меню, работает и здесь - lore, модификаторы атрибутов, кастомные NBT, прочность, модель. Полный список см. в [документации формата предметов](/docs/ru/general/item-format/). + +## Как попробовать + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give 2000`. +4. Введите `/ame_store` в игре. diff --git a/src/content/docs/ru/examples/donate/tiered-ranks.mdx b/src/content/docs/ru/examples/donate/tiered-ranks.mdx new file mode 100644 index 0000000..3f25488 --- /dev/null +++ b/src/content/docs/ru/examples/donate/tiered-ranks.mdx @@ -0,0 +1,46 @@ +--- +title: Донат-ранги по уровням +description: Донат-магазин из 4 рангов. Каждый ранг по-разному выглядит для игроков, у которых он уже есть, и для тех, у кого его пока нет. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Стандартная раскладка донат-магазина: плитки рангов по возрастанию, у каждой два варианта - "Куплено", если у игрока уже есть нужное право, и цена + список бонусов, если права нет. Сделано на паттерне дублирующих предметов: по одному варианту на каждое состояние ранга. + + + +## Что показывает + +- Как масштабировать паттерн двух предметов на много уровней +- Алиасы команд через `command: [..., ...]` (так что меню открывают и `/ame_donate`, и `/ame_support`) +- Список бонусов в lore у каждого ранга +- Куда вставить реальную логику покупки (в этом примере просто отправляется сообщение в чат) + +## Как складываются два варианта + +В каждом слоте ранга объявлено по два предмета. Вариант HAS_RANK имеет `rules { permission: "myserver.rank.knight" }` и показывает "Куплено" + "Подумайте об апгрейде ниже". Вариант NO_RANK без правил, показывает бонусы и цену. + +Правило отрисовки то же, что и у [Магазина с проверкой ранга](../shops/rank-gated-shop): предметы обходятся в порядке объявления; в каждом слоте отрисовывается первый предмет, у которого правила прошли. HAS_RANK объявлен первым, поэтому если у игрока есть право - выигрывает он. + +## Как привязать реальную покупку + +Настоящие донат-магазины интегрируются с платёжкой. Типичные варианты: + +- **Внешний URL**: в click-действии отправляем `&aЗайдите на https://example.org/donate, чтобы купить`. Игрок покупает на сайте, ранг выдаётся через API. +- **Внутриигровая валюта**: ставим правило `money` и действие `takeMoney`, как в обычном магазине, потом выдаём ранг через `addGroup: "knight"`. +- **Player Points / токены**: то же самое, что money, но через другой провайдер. Можно прямо указать провайдера: `takeMoney: { amount: 5, provider: "playerpoints" }`. + +В этом примере используется вариант с URL, потому что он самый универсальный. Заменяйте блок click под свой магазин. + +## Как добавить пятый уровень + +Скопируйте пару (HAS_RANK + NO_RANK) для нового уровня, поменяйте слот на свободную позицию (хорошо подходят слоты 27 или 29 в 4-м ряду), обновите имя права, бонусы и цену. + +## Как попробовать + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_donate` или `/ame_support` в игре. +4. Выдайте себе право (например, `lp user permission set myserver.rank.knight true`), чтобы увидеть вариант HAS_RANK. diff --git a/src/content/docs/ru/examples/hub-and-nav/breadcrumb-chain.mdx b/src/content/docs/ru/examples/hub-and-nav/breadcrumb-chain.mdx new file mode 100644 index 0000000..9be2828 --- /dev/null +++ b/src/content/docs/ru/examples/hub-and-nav/breadcrumb-chain.mdx @@ -0,0 +1,38 @@ +--- +title: Цепочка хлебных крошек +description: Три вложенных меню с ручной навигацией назад. Простейший паттерн для деревьев меню. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Три меню, связанных через `openMenu`, на каждом уровне есть кнопка назад на предыдущий. Показывает, как строить деревья меню в несколько уровней без чего-то сложнее базового действия openMenu. + + + +## Чему учит + +- Построение более глубоких деревьев меню через ручные цепочки openMenu +- Кнопки назад на каждом уровне, захардкоженные на родителя + +## Ручная против контекстной навигации назад + +Кнопка назад на каждом уровне хардкодит имя родительского меню: + +```hocon +{ slot: 0, name: "&eНазад на Уровень 2", click { openMenu: "ame_chain_l2" } } +``` + +Просто, предсказуемо, работает для древовидных структур, где у каждого меню ровно один родитель. Минус: если `ame_chain_l2` открывается из нескольких родителей (скажем, и с Уровня 1, и из под-хаба), кнопка назад всегда возвращает на Уровень 1, игнорируя реальный путь игрока. + +Для меню в форме DAG, где меню можно достичь из разных мест и кнопка назад должна учитывать реальный путь, используй `openMenuCtx`. Он протаскивает контекст по цепочке меню. См. [документацию по input активатора/меню](/docs/ru/advanced/input/) про механику `openMenuCtx`. + +В 95% случаев простого дерева достаточно - захардкоженные кнопки назад держат всё предсказуемым. + +## Попробовать + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_chain` в игре. +4. Кликни "Глубже" дважды. Кликни назад дважды. diff --git a/src/content/docs/ru/examples/hub-and-nav/index.mdx b/src/content/docs/ru/examples/hub-and-nav/index.mdx new file mode 100644 index 0000000..1b2ecbf --- /dev/null +++ b/src/content/docs/ru/examples/hub-and-nav/index.mdx @@ -0,0 +1,18 @@ +--- +title: Хаб и навигация +description: Главные хабы и под-меню - openMenu, паттерны back-button, цепочки через openMenuCtx. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Главные хабы и под-меню демонстрируют паттерны навигации через `openMenu`, кнопки "назад" и цепочки через `openMenuCtx`. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/hub-and-nav/main-hub.mdx b/src/content/docs/ru/examples/hub-and-nav/main-hub.mdx new file mode 100644 index 0000000..8cb6a38 --- /dev/null +++ b/src/content/docs/ru/examples/hub-and-nav/main-hub.mdx @@ -0,0 +1,46 @@ +--- +title: Главный хаб +description: Меню-лаунчер, открывающее любое из меню пака примеров из одного места. Тот же паттерн подходит для любого "выбери категорию". +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Хаб - это просто список кнопок, каждая из которых открывает другое меню. Полезен как главная команда `/menu` сервера или как способ организовать большой набор подфункций. Этот пример ссылается на остальные меню пака, так что его можно использовать как лаунчер при изучении. + +## Чему учит + +- Действие `openMenu` - единственное, что делают кнопки +- Простой layout на 3 ряда с рамкой, заголовком и рядом плиток +- Переиспользуемый паттерн для любого UI типа "выбери категорию" + + + +## Как работает + +Каждая плитка одинаковая по форме: предмет с `material:`, `name:`, `lore:` и `click { openMenu: "ame_<имя>" }`. Никаких правил, никаких действий кроме навигации, никаких проверок денег. Задача хаба - только диспетчеризация. + +Заголовок в слоте 4 чисто декоративный - так же, как в [Магазине тортов](../shops/cake-shop). Без обработчика клика. + +## Кастомизация + +Чтобы добавить новую плитку: + +1. Выбери свободный слот (10-16 в этом layout подходят). +2. Скопируй блок любой плитки. +3. Поменяй `material:`, `name:`, `lore:` и цель `openMenu:`. + +Хаб не зависит от того, какие меню реально существуют. Если плитка указывает на несуществующее меню, клик просто покажет предупреждение "menu not found" - удобно для подготовки будущих меню до их сборки. + +## Попробовать + +После установки пака примеров: + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_hub` в игре. diff --git a/src/content/docs/ru/examples/hub-and-nav/sub-hub.mdx b/src/content/docs/ru/examples/hub-and-nav/sub-hub.mdx new file mode 100644 index 0000000..fdac541 --- /dev/null +++ b/src/content/docs/ru/examples/hub-and-nav/sub-hub.mdx @@ -0,0 +1,45 @@ +--- +title: Под-хаб +description: Вторичный хаб с кнопкой назад в главный хаб. Стандартное меню "ещё опции". +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Когда главный хаб переполняется, вынеси лишние функции в отдельный хаб. Этот пример следует самому распространённому паттерну: тот же layout, что и у главного хаба, но с кнопкой назад на родителя. + + + +## Чему учит + +- Паттерн кнопки назад через `openMenu` на имя родителя +- Различие "назад" (просто подняться по дереву меню) и "закрыть" (выйти из UI меню) +- Возможность открыть под-хаб напрямую через свою команду в дополнение к навигации по цепочке + +## Два способа "вернуться назад" + +Подход 1 (используется здесь): захардкодить имя родителя. + +```hocon +{ + slot: 18 + material: ARROW + name: "&eНазад в главный хаб" + click { openMenu: "ame_hub" } +} +``` + +Дёшево, просто, работает. Ограничение: если в этот под-хаб ведут несколько меню, кнопка назад всегда возвращает на `ame_hub`, даже если игрок пришёл откуда-то ещё. + +Подход 2 (см. [Цепочка хлебных крошек](./breadcrumb-chain)): использовать `openMenuCtx` и дать контексту активатора протащить "предыдущее меню" по цепочке. Кнопка назад тогда динамически вернёт туда, откуда игрок пришёл. + +В 95% случаев захардкодить родителя нормально. Оставь `openMenuCtx` для действительно глубоких деревьев меню. + +## Попробовать + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_hub`, чтобы начать с главного хаба, или `/ame_subhub`, чтобы попасть сюда напрямую. +4. Кликни по плитке, чтобы открыть целевое меню. +5. Кликни "Назад в главный хаб", чтобы вернуться. diff --git a/src/content/docs/ru/examples/index.mdx b/src/content/docs/ru/examples/index.mdx new file mode 100644 index 0000000..cc56ee1 --- /dev/null +++ b/src/content/docs/ru/examples/index.mdx @@ -0,0 +1,38 @@ +--- +title: Примеры +description: Каталог готовых меню AbstractMenus - копируй, кидай в плагин, подгоняй под себя. +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; + +Examples Pack - каталог из ~46 готовых меню, разложенных по типам использования. Каждое можно скачать одним `.conf` файлом или zip-бандлом для распаковки на сервере. + +## Как пользоваться + +У каждого меню своя страница: подсвеченный код, бейджи "что демонстрирует и какие плагины нужны", кнопка **Download** для одного файла, кнопка **Bundle** для zip с меню + общими шаблонами + `INSTALL.txt` с инструкцией. + +Структура zip совпадает со структурой на сервере, так что распаковка в `plugins/AbstractMenus/menus/example/` сразу даёт рабочий набор. У всех команд активаторов префикс `ame_` (например `/ame_cake_shop`) - не пересекается с твоими реальными командами. + +## Категории + + + + + + + + + + + + + + + +## Общие шаблоны + +Все меню могут опционально подключать `_shared/templates.conf` (~80 строк) с пресетами звуков, deny-блоков, бордеров, кнопок навигации, rule-шорткатов и общим хедером. Bundle сам добавляет файл в zip когда нужно. + +## Совместимость + +Все примеры рассчитаны на **AbstractMenus 2.0.0+** и **Minecraft 1.21+**. Бейджи на странице каждого меню перечисляют внешние плагины (Vault, LuckPerms, WorldGuard, Citizens и т.д.). diff --git a/src/content/docs/ru/examples/info-pages/faq.mdx b/src/content/docs/ru/examples/info-pages/faq.mdx new file mode 100644 index 0000000..36c34a3 --- /dev/null +++ b/src/content/docs/ru/examples/info-pages/faq.mdx @@ -0,0 +1,49 @@ +--- +title: FAQ +description: Список вопросов, по клику открывается подменю с ответом. Один файл с несколькими меню и кнопкой "Назад" на каждой странице ответа. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +FAQ по сути - дерево навигации: список вопросов в корне, под каждый ответ - своё подменю. В этом примере используется блок `menus { ... }` с несколькими меню в одном файле, чтобы держать все четыре экрана в одном конфиге, а связи между ними - через вызовы `openMenu`. Общий шаблон `${buttonBack}` получает переопределение на каждой странице, чтобы вести обратно на главную FAQ. + + + +## Чему учит + +- Использование `menus { ... }`, чтобы объявить хаб и несколько экранов с ответами в одном файле +- Переопределение полей общего шаблона на месте использования - `${buttonBack} { slot: 18, click { openMenu: "..." } }` +- Паттерн "страница ответа": крупный предмет с lore + кнопка "Назад", больше никакой интерактивности +- Один из вопросов ведёт в другое существующее меню (вопрос про правила открывает меню [Правила сервера](./rules)) + +## Почему FAQ удобно держать в одном файле с несколькими меню + +FAQ из 10 вопросов в виде 10 отдельных файлов - неаккуратно: 10 путей, 10 строк `include` под общие шаблоны. Если собрать их в одном блоке `menus { ... }`, связанные экраны лежат вместе, а редактирование FAQ становится операцией над одним файлом. + +Соглашение об именах важно: каждое меню получает уникальное имя в пределах сервера. Берите `ame_faq`, `ame_faq_a1`, `ame_faq_a2` и т.д., чтобы не пересечься с другими меню из примеров или собственными конфигами пользователя. + +## Переопределение `${buttonBack}` + +Общий шаблон `buttonBack` определяет стрелку с `closeMenu: true`. В FAQ нам нужно вместо этого вернуться на главную. Переопределим блок click на месте использования: + +```hocon +${buttonBack} { slot: 18, click { openMenu: "ame_faq" } } +``` + +`${buttonBack}` приносит материал, имя и lore. Переопределение заменяет блок click. Тот же паттерн работает для любого общего шаблона, который нужно специализировать под конкретное место использования. + +## Добавить вопрос + +Две правки на новый вопрос: + +1. В список items меню `ame_faq` добавьте бумажную плитку, ведущую на имя нового меню с ответом. +2. Добавьте новый блок `ame_faq_aN` в конец обёртки `menus { ... }` с содержимым ответа. + +## Попробовать + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_faq` в игре. +4. Кликните по любому вопросу. Прочитайте ответ. Кликните стрелку "Назад", чтобы вернуться. diff --git a/src/content/docs/ru/examples/info-pages/index.mdx b/src/content/docs/ru/examples/info-pages/index.mdx new file mode 100644 index 0000000..2fa4b71 --- /dev/null +++ b/src/content/docs/ru/examples/info-pages/index.mdx @@ -0,0 +1,18 @@ +--- +title: Инфо-страницы +description: Read-only меню с правилами сервера, внешними ссылками, паг FAQ. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Read-only меню для отображения правил сервера, внешних ссылок и пагинированного FAQ. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/info-pages/rules.mdx b/src/content/docs/ru/examples/info-pages/rules.mdx new file mode 100644 index 0000000..61958fb --- /dev/null +++ b/src/content/docs/ru/examples/info-pages/rules.mdx @@ -0,0 +1,51 @@ +--- +title: Правила сервера +description: Меню только для чтения с правилами сервера. Игроки открывают, листают, закрывают. Никакой логики обработки кликов. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Самое простое меню: только текст. Четыре правила, каждое - бумажный предмет с многострочным описанием в lore. Без обработчиков кликов, правил и действий. Единственный интерактивный элемент - кнопка закрытия. + +## Чему учит + +- Предметы без `click {}` пассивные - игрок может навести курсор, но клик ничего не делает +- Многострочный `lore: [ ... ]` для развёрнутого текста на одном предмете +- Меню на 5 рядов через `size: 5` и как номера слотов соотносятся с рядами +- Сочетание декоративных рамок с содержимым + + + +## Как это работает + +`size: 5` даёт 45 слотов (ряды 0..4). Правила лежат в среднем ряду (ряд 2, слоты 18-26) через слот, чтобы было свободнее. + +На предметах правил нет блоков `click`, потому что кликать не на что. Игроки просто наводят курсор и читают lore. + +Кнопка закрытия - единственный интерактивный предмет, взятый из общего шаблона `${buttonClose}`. + +## Кастомизация + +Чтобы добавить пятое правило, положите ещё один бумажный предмет в свободный слот ряда 2 или 3. Номера слотов идут слева направо в пределах ряда: + +- Ряд 0: 0-8 (верхняя рамка в этом примере) +- Ряд 1: 9-17 +- Ряд 2: 18-26 (правила) +- Ряд 3: 27-35 +- Ряд 4: 36-44 (нижняя рамка) + +Чтобы переформулировать правила, просто отредактируйте поля `name:` и `lore:`. Менять код не нужно. + +## Попробовать + +После установки набора примеров: + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_rules` в игре. diff --git a/src/content/docs/ru/examples/info-pages/server-links.mdx b/src/content/docs/ru/examples/info-pages/server-links.mdx new file mode 100644 index 0000000..e998cfe --- /dev/null +++ b/src/content/docs/ru/examples/info-pages/server-links.mdx @@ -0,0 +1,51 @@ +--- +title: Ссылки сервера +description: Кликните по плитке, чтобы открыть книгу с кликабельной внешней ссылкой. Подходит для приглашений в Discord, ссылок на сайт, GitHub, страницу доната и т.д. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Чат Minecraft позволяет игрокам кликать по URL в сообщениях или книгах. Самый простой способ показать внешние ссылки из меню - открывать книгу в одну страницу под каждую ссылку: книга отображает URL как кликабельный текст прямо в интерфейсе игрока. + + + +## Почему книги, а не сообщения в чате + +Сообщение в чате с URL - нормально, но его легко проскроллить. Книга открывается в поле зрения игрока, выглядит более явно, а URL рендерится как большая кликабельная кнопка. Для меню "ссылки" книги дают лучший UX. + +## Формат openBook + +```hocon +openBook { + author: "MyTestServer" + title: "&5Discord" + pages: [ + "Кликните по ссылке ниже, чтобы открыть приглашение:\n\nhttps://discord.gg/example" + ] +} +``` + +`pages` - список строк; каждая строка - одна страница. Переносы строк внутри строки переносят на новую строку на этой странице. Обычные URL автоматически становятся кликабельными при отрисовке книги. + +Для каталога ресурсов на несколько страниц перечислите больше строк: + +```hocon +pages: [ + "Страница 1: приглашение в Discord\nhttps://discord.gg/...", + "Страница 2: сайт\nhttps://example.org", + "Страница 3: GitHub\nhttps://github.com/..." +] +``` + +## Закрытие меню + +Каждый блок click вызывает `closeMenu: true` после открытия книги. Иначе меню осталось бы открытым позади книги - запутывает. + +## Попробовать + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_links` в игре. +4. Кликните по любой плитке. Откроется книга с кликабельным URL внутри. diff --git a/src/content/docs/ru/examples/kits-and-rewards/basic-kit.mdx b/src/content/docs/ru/examples/kits-and-rewards/basic-kit.mdx new file mode 100644 index 0000000..05a2616 --- /dev/null +++ b/src/content/docs/ru/examples/kits-and-rewards/basic-kit.mdx @@ -0,0 +1,61 @@ +--- +title: Базовый кит +description: Меню стартового кита - один клик, и игрок получает набор предметов плюс опыт. Без кулдауна, без правил, только награды. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Самая простая форма меню с наградой: карточка с описанием, кнопка "забрать" и кнопка закрытия. Клик по кнопке - игрок получает набор предметов, немного опыта, звуковой эффект и сообщение-подтверждение. + +## Что показывает пример + +- Несколько действий подряд внутри одного блока `click { actions { ... } }` +- Список-форма `itemAdd:` для выдачи нескольких предметов сразу +- Действие `giveXp` для ванильных очков опыта +- Отложенное `closeMenu: 20` (закрыть через 20 тиков = 1 секунда, чтобы игрок успел увидеть сообщение об успехе до закрытия меню) + + + +## Как это работает + +Блок click кнопки "забрать" выполняет пять действий по порядку: + +1. **`itemAdd: [ ... ]`** - список-форма позволяет положить в инвентарь игрока несколько предметов за один раз. +2. **`giveXp: 50`** - даёт 50 опыта через настроенный levels-провайдер (по умолчанию ванильный опыт). +3. **`sound: ${successSound}`** - проигрывает звук успеха из общих шаблонов. +4. **`message:`** - подтверждение в чат. +5. **`closeMenu: 20`** - закрытие через 20 тиков (1 секунда). Число вместо `true` добавляет задержку, чтобы игрок успел заметить, что действия выполнились. + +Никакой проверки кулдауна тут нет. Любой может жать на кнопку и получать киты бесконечно. Чтобы повесить на это кулдаун, посмотри пример с ежедневным китом - там добавлен `setVarp` с временной меткой и правило `if`, сравнивающее её с текущим временем. + +## Кастомизация + +Замени любой предмет в списке `itemAdd` на нужные тебе материалы. Добавь сколько угодно записей. Поле `count:` опциональное, по умолчанию 1. + +Для зачарованных предметов работает поле `enchantments` на предмете: + +```hocon +itemAdd: [ + { + material: DIAMOND_SWORD + enchantments { + sharpness: 5 + unbreaking: 3 + } + } +] +``` + +## Попробовать + +После установки пакета примеров: + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_kit` в игре. diff --git a/src/content/docs/ru/examples/kits-and-rewards/daily-kit-cooldown.mdx b/src/content/docs/ru/examples/kits-and-rewards/daily-kit-cooldown.mdx new file mode 100644 index 0000000..276f334 --- /dev/null +++ b/src/content/docs/ru/examples/kits-and-rewards/daily-kit-cooldown.mdx @@ -0,0 +1,89 @@ +--- +title: Ежедневный кит с кулдауном +description: Ежедневно забираемые киты на временных переменных. Суффикс "::1d" в setVarp автоматически снимает кулдаун через 24 часа. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Классический паттерн ежедневного кита: игрок забирает кит, блокируется на 24 часа, потом снова может забрать. Построено на временных переменных (автоистечение через заданный срок), паттерне с двумя предметами (разный вид во время кулдауна) и подстановке шаблонов, чтобы метаданные кита расшаривались между обоими состояниями. + +## Что показывает пример + +- Сокращение для временных переменных: `setVarp: "name::value::1d"` (автоистечение через 1 день) +- Плейсхолдер `%varpt_:name%` для отображения "оставшееся время" +- Подстановка шаблона: `${defaultKitTemplate}` переиспользуется в состояниях ДОСТУПЕН и КУЛДАУН +- Расширение lore: `lore: ${defaultKitTemplate.lore} [ ... extra lines ... ]` дописывает к lore из шаблона +- Правило `group:` для гейтинга через LuckPerms на VIP-ките + + + +## Как работают временные переменные + +`setVarp: "ame_kit_default::1::1d"` парсится так: + +- **Имя переменной**: `ame_kit_default` +- **Значение**: `1` (само значение не используется, важен только факт существования) +- **Срок**: `1d` (один день - другие единицы: `s`, `m`, `h`, `d`, `w`) + +Через 24 часа переменная автоматически удаляется. Правило `existVarp` после этого даёт false, предмет с кулдауном пропадает, и снова показывается предмет ДОСТУПЕН. + +Плейсхолдер `%varpt_:ame_kit_default%` возвращает оставшееся до истечения время в человекочитаемом виде ("23h 47m" или похожее). Удобно для сообщений "доступно через X". + +## Подстановка шаблона + расширение lore + +Каждый кит один раз в начале файла определяет свой слот, материал, имя и базовый lore: + +```hocon +defaultKitTemplate { + slot: 11 + material: WOODEN_SWORD + name: "&aDefault Kit" + lore: [ "&7Includes:", "&7- Leather armor set", ... ] +} +``` + +Оба предмета - ДОСТУПЕН и КУЛДАУН - ссылаются на этот шаблон: + +```hocon +# ДОСТУПЕН: дополняет lore строкой "Click to claim" +${defaultKitTemplate} { + lore: ${defaultKitTemplate.lore} [ "", "&aClick to claim" ] + click { ... claim logic ... } +} + +# КУЛДАУН: дополняет lore строкой с оставшимся временем +${defaultKitTemplate} { + material: GRAY_DYE + lore: ${defaultKitTemplate.lore} [ "", "&cAvailable in &e%varpt_:ame_kit_default%" ] + rules { existVarp: "ame_kit_default" } +} +``` + +Синтаксис `${defaultKitTemplate.lore} [ ... ]`: взять существующий массив lore и дописать в конец новые элементы. Базовый lore не нужно копипастить. + +Когда переменной не существует, у предмета КУЛДАУН проваливается правило `existVarp`, и он не отрисовывается. На его место встаёт предмет ДОСТУПЕН (без правил). Когда переменная есть, отрисовывается КУЛДАУН, а ДОСТУПЕН подавляется по логике конфликта слотов. + +## Кастомизация + +Чтобы добавить ещё тиры китов: + +1. Добавь блок `xxxKitTemplate` в начале со своими `slot:`, `material:`, `name:`, базовым `lore:`. +2. Добавь предмет ДОСТУПЕН на основе шаблона + `click { ... }` с содержимым кита и уникальным именем `setVarp`. +3. Добавь предмет КУЛДАУН на основе шаблона + `rules { existVarp: "..." }` с тем же именем переменной. + +Для гейтинга по правам на конкретный тир добавь `rules { group: "tier_name" }` в click-блок (или `permission: "..."` для более простой проверки). + +## Попробовать + +После установки пакета примеров: + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_daily_kit` в игре. +4. Забери обычный кит. Предмет станет серым и покажет "Доступен через 23h 59m..." diff --git a/src/content/docs/ru/examples/kits-and-rewards/index.mdx b/src/content/docs/ru/examples/kits-and-rewards/index.mdx new file mode 100644 index 0000000..8c018cd --- /dev/null +++ b/src/content/docs/ru/examples/kits-and-rewards/index.mdx @@ -0,0 +1,18 @@ +--- +title: Киты и награды +description: Базовые и тиерные киты с кулдаунами через temporal-vars. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Базовые и тиерные киты с per-player кулдаунами через temporal-переменные и time-based правила. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/kits-and-rewards/tiered-kits.mdx b/src/content/docs/ru/examples/kits-and-rewards/tiered-kits.mdx new file mode 100644 index 0000000..5c8c08e --- /dev/null +++ b/src/content/docs/ru/examples/kits-and-rewards/tiered-kits.mdx @@ -0,0 +1,35 @@ +--- +title: Киты по уровням +description: Три тира китов (Default / VIP / Elite), каждый закрыт правом доступа. Кулдауна нет - киты можно брать сколько угодно раз. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Простой аналог [Ежедневного кита с кулдауном](./daily-kit-cooldown). Та же идея с тирами (default / VIP / Elite), но без блокировки через временные переменные - киты можно забирать в любой момент. Удобно для "стартовых наборов" или "всегда доступных бонусов для премиум-рангов". + + + +## Что показывает пример + +- Гейтинг по правам на уровне клика через `rules { permission: "..." }` +- Сборка `denyActions: ${denyNoPerm}` из общих шаблонов для отказа +- Список-форма `itemAdd: [ ... ]` для выдачи набора из нескольких предметов + +## Почему здесь нет паттерна с двумя предметами? + +Это меню показывает ВСЕ три кита всем. VIP- и Elite-киты видны независимо от того, может ли игрок их забрать - проверка правила происходит на клик, а не на отрисовку. Если обычный игрок кликнет VIP, сработает `denyActions` и он увидит сообщение "нет прав". + +Это сделано намеренно. Игрок видит, что мог бы открыть - это мягкий пуш в сторону донат-рангов. Паттерн с двумя предметами (полностью прятать заблокированные плитки) использован в [Тирах донат-рангов](../donate/tiered-ranks); выбирай то, что подходит твоему серверу. + +## Добавить тир + +Скопируй блок любого кита, поменяй слот, имя, lore, ноду прав и содержимое itemAdd. Для большего визуального разнообразия меняй `material:` под тир (NETHERITE_SWORD для Mythic и т. д.). + +## Попробовать + +1. Положи бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введи `/ame_kit_tiers` в игре. +4. Обычный кит работает у всех. Выдай себе `abstractmenus.vip` или `abstractmenus.elite`, чтобы забрать остальные. diff --git a/src/content/docs/ru/examples/shops/cake-shop.mdx b/src/content/docs/ru/examples/shops/cake-shop.mdx new file mode 100644 index 0000000..2a02a71 --- /dev/null +++ b/src/content/docs/ru/examples/shops/cake-shop.mdx @@ -0,0 +1,48 @@ +--- +title: Магазин тортов +description: Минимальный магазин - три предмета, продаются за внутриигровые деньги. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Магазин тортов - самый простой возможный магазин. Три предмета, три цены, один клик для покупки. Если вы никогда не писали меню в AbstractMenus - читайте сначала это. + +## Что показывает + +- Правило `money` для проверки "хватает ли игроку денег" +- Действие `takeMoney` для списания цены +- Действие `itemAdd` для выдачи купленного предмета +- Блок `denyActions`, который срабатывает, если правила не прошли +- Переиспользование общих шаблонов через подстановку `${...}` + + + +## Как это работает + +У меню размер 3 (один ряд сундука плюс рамки на 18 слотов сверху и снизу). Три ряда стеклянных панелей-рамок добавлены через ссылку `${border_black}` из общих шаблонов с указанием диапазона слотов. + +В каждом слоте предмета объявлен блок `click` из двух частей: + +1. **`rules`** - список условий. Здесь `money: 100` означает "у игрока должно быть как минимум $100". Если хоть одно правило не прошло - вместо action'ов выполняются те, что в `denyActions`. +2. **`actions`** - успешный путь. Списываем деньги, выдаём предмет, проигрываем подтверждающий звук и шлём сообщение в чат. + +Подстановка `denyActions: ${deny_no_money}` подтягивает заранее собранную последовательность действий из `_shared/templates.conf` - проигрывает звук отказа и шлёт сообщение "У вас недостаточно денег". Переиспользуется во всех экономических меню. + +## Попробовать + +После установки набора примеров: + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_cake_shop` в игре. + +## Дальше + +- Смотрите [магазин по категориям](./paginated-shop), если предметов много и они разнесены по нескольким страницам. +- Смотрите [магазин с проверкой ранга](./rank-gated-shop) для цен в зависимости от прав. diff --git a/src/content/docs/ru/examples/shops/index.mdx b/src/content/docs/ru/examples/shops/index.mdx new file mode 100644 index 0000000..4e31973 --- /dev/null +++ b/src/content/docs/ru/examples/shops/index.mdx @@ -0,0 +1,18 @@ +--- +title: Магазины +description: Меню купли/продажи - money rule, takeMoney, GeneratedMenu пагинация, NPC-вендоры. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Магазины показывают полный путь от "одно-предметный шоп" до "пагинированный каталог с проверкой прав и NPC-вендорами". + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/shops/npc-vendor.mdx b/src/content/docs/ru/examples/shops/npc-vendor.mdx new file mode 100644 index 0000000..5a92b14 --- /dev/null +++ b/src/content/docs/ru/examples/shops/npc-vendor.mdx @@ -0,0 +1,65 @@ +--- +title: NPC-торговец +description: Магазин, который открывается, когда игрок кликает правой кнопкой по конкретному NPC из Citizens. Тот же паттерн магазина, что у Магазина тортов, только активатор другой. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickNPC` от Citizens - популярный активатор на RPG-серверах. Игрок подходит к NPC-торговцу, кликает правой - открывается меню магазина. Меню может читать контекст NPC через плейсхолдеры `%activator_npc_*%` - в этом примере имя NPC подставляется в заголовок, чтобы торговец казался "своим". + + + +## Что показывает + +- Активатор `clickNPC` с ID NPC из Citizens +- Плейсхолдер `%activator_npc_name%` для персонализированных заголовков +- Как плейсхолдеры из контекста активатора пробрасываются в предметы меню + +## Настройка + +Этот активатор работает только если установлен [Citizens](https://www.spigotmc.org/resources/citizens.13811/) и существует хотя бы один NPC с указанным ID. Чтобы узнать ID NPC: + +1. Посмотрите на NPC. +2. Выполните `/npc sel`. +3. Выполните `/npc`, чтобы увидеть инфу по выбранному NPC, включая ID. + +Замените `clickNPC: [1]` на реальный ID вашего NPC. Если несколько NPC должны открывать один и тот же магазин: + +```hocon +clickNPC: [1, 5, 12] +``` + +## Плейсхолдеры из контекста NPC + +Внутри меню `%activator_npc_*%` отдают данные кликнутого NPC: + +| Плейсхолдер | Возвращает | +|---|---| +| `%activator_npc_id%` | Числовой ID NPC | +| `%activator_npc_name%` | Отображаемое имя NPC | +| `%activator_npc_world%` | Мир, в котором находится NPC | + +В примере `%activator_npc_name%` используется в заголовке, поэтому заголовок меню превращается в "Торговец BlacksmithBob" или какое там у NPC имя. + +## Чем это лучше `command:` для торговцев + +Команда `/shop` - универсальная: все используют один синтаксис, чтобы открыть одно и то же меню. Правый клик по NPC привязывает магазин к конкретной точке в мире и даёт торговцу визуальную личность. Игроки также не могут случайно открыть магазин из любой точки - они физически идут к NPC. Лучше для отыгрыша, меньше спама командами. + +Чтобы был гибрид (открытие по клику на NPC ИЛИ командой) - в блок activators кладите оба: + +```hocon +activators { + command: "ame_vendor" + clickNPC: [1] +} +``` + +## Попробовать + +1. Установите Citizens. +2. Заспавните NPC: `/npc create Bob`, потом посмотрите на него, `/npc sel`, `/npc` чтобы подтвердить ID. +3. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. Поправьте `clickNPC: [1]` на ID вашего NPC. +4. `/am reload`. +5. Кликните правой по NPC. diff --git a/src/content/docs/ru/examples/shops/paginated-shop.mdx b/src/content/docs/ru/examples/shops/paginated-shop.mdx new file mode 100644 index 0000000..1f770e1 --- /dev/null +++ b/src/content/docs/ru/examples/shops/paginated-shop.mdx @@ -0,0 +1,58 @@ +--- +title: Магазин по категориям +description: Меню-хаб с плитками категорий, открывающими отдельные подмагазины. В каждом из них показан паттерн покупки. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Когда в магазине больше предметов, чем удобно поместить на одну страницу, естественный паттерн - хаб плюс подмагазины по категориям. В этом примере структура показана на трёх меню в одном файле: хаб и две категории. + +## Что показывает + +- Файлы с несколькими меню через обёртку `menus { ... }` +- Навигацию между меню через `openMenu` +- Повторение паттерна покупки по категориям без дублирования кода +- Сочетание статических рамок и предметов магазина +- Шорткат `flags: HIDE_ATTRIBUTES` для иконок инструментов и оружия + + + +## Как это работает + +В файле через обёртку `menus { ... }` объявлены три именованных меню: + +- `ame_shop` - главный хаб. На нём активатор `command: "ame_shop"`, показывает плитки категорий. +- `ame_shop_food` и `ame_shop_tools` - подмагазины, открывающиеся из хаба. + +Каждая плитка в хабе - это просто предмет с `click { openMenu: "ame_shop_food" }`. Никаких правил на деньги, никаких action'ов помимо самой навигации. Подмагазины открываются по имени. + +Внутри подмагазина предметы построены по тому же паттерну, что и в [магазине тортов](./cake-shop): `rules { money: ... }`, блок `actions {}`, который списывает деньги и выдаёт предмет, и `denyActions: ${denyNoMoney}` из общих шаблонов на случай отказа. + +Кнопка "назад" использует `openMenu: "ame_shop"`, чтобы вернуться в хаб. Встроенного "назад" нет. Навигация по меню - это просто вызов `openMenu` с именем предыдущего меню. + +## Масштабирование + +Добавление новой категории - механическое: + +1. Добавьте новую плитку в список items хаба с `click { openMenu: "ame_shop_" }`. +2. Скопируйте любой блок подмагазина (например, `ame_shop_food`), переименуйте в `ame_shop_`, замените предметы. +3. Перезагрузите меню через `/am reload`. + +## Попробовать + +После установки набора примеров: + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_shop` в игре. + +## Дальше + +- [Магазин тортов](./cake-shop) - самый простой одностраничный магазин. +- [Документация по генерации](/docs/ru/advanced/generation/) - пагинация по динамическому каталогу (когда предметы приходят из кода - например, онлайн-игроки или миры). diff --git a/src/content/docs/ru/examples/shops/rank-gated-shop.mdx b/src/content/docs/ru/examples/shops/rank-gated-shop.mdx new file mode 100644 index 0000000..21d663d --- /dev/null +++ b/src/content/docs/ru/examples/shops/rank-gated-shop.mdx @@ -0,0 +1,60 @@ +--- +title: Магазин с проверкой ранга +description: Магазин, где VIP видят другие цены, нежели обычные игроки. Боевой паттерн HAS/NO_PERM с двумя предметами в одном слоте, как на реальных продакшен-серверах. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Многие реальные магазины показывают разные цены разным рангам - VIP получают скидку, донатеры видят эксклюзивные предметы и так далее. Реализация - два предмета в одном слоте: у первого есть правило на право, второй идёт фолбэком. + + + +## Что показывает + +- Паттерн с двумя предметами в одном слоте и правилом на право +- Цены по рангам без сложных правил `if` +- Переиспользуемые обработчики кликов через подстановки `${discountClick}` / `${regularClick}` +- Как взаимодействуют короткое замыкание правил и порядок объявления слотов + +## Как работают два предмета в одном слоте + +В одном слоте объявлены два предмета: + +```hocon +# Вариант A: показывается только VIP +{ slot: 13, name: "Golden Apple (VIP price)", rules { permission: "...vip" }, click: ${discountClick} } + +# Вариант B: фолбэк для всех остальных +{ slot: 13, name: "Golden Apple", click: ${regularClick} } +``` + +Когда меню рендерится, плагин проходит по списку предметов. Для слота 13 он находит вариант A, проверяет его правила и: + +- Использует A, если правило прошло (VIP видит вариант со скидкой) +- Пропускает A и ищет следующее объявление для слота 13. Находит B. У B правил нет. Рендерится B. + +Это БОЕВОЙ паттерн с реальных магазинов. Масштабируется на много рангов (4-уровневые донат-магазины и т.д.) - по одному варианту на ранг, в порядке приоритета, с самым высоким рангом в правиле первым. + +## Почему это лучше `if` для проверки рангов + +Правило `if` могло бы выразить ту же логику, но добавило бы две проблемы: + +1. Сам отображаемый предмет не менялся бы - менялось бы только действие на клик. Для визуального различия всё равно нужны два предмета. +2. `if` парсится как выражение на каждом рендере. Два предмета по правам обходятся дешевле. + +`if` нужен, когда сравниваете числовые/строковые значения, которые нельзя выразить встроенным правилом (математика по уровню, кастомные значения переменных). Для "игрок в ранге X" - паттерн с двумя предметами и правом. + +## Расширение на больше уровней + +Добавляйте варианты в порядке приоритета. Mythic > Donor > VIP > обычный: + +```hocon +{ slot: 13, ..., rules { permission: "myplugin.mythic" }, click: ${mythicClick} } +{ slot: 13, ..., rules { permission: "myplugin.donor" }, click: ${donorClick} } +{ slot: 13, ..., rules { permission: "myplugin.vip" }, click: ${vipClick} } +{ slot: 13, ..., click: ${regularClick} } # фолбэк - без правил +``` + +Порядок важен: ранг с самым высоким приоритетом - первым. Побеждает первый подходящий вариант. diff --git a/src/content/docs/ru/examples/shops/sell-and-buy-shop.mdx b/src/content/docs/ru/examples/shops/sell-and-buy-shop.mdx new file mode 100644 index 0000000..954601c --- /dev/null +++ b/src/content/docs/ru/examples/shops/sell-and-buy-shop.mdx @@ -0,0 +1,69 @@ +--- +title: Магазин покупки и продажи +description: Одно меню, две операции - левый клик покупает предмет, правый продаёт. Несимметричные цены имитируют наценку торговца. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Магазин в стиле торговца, где каждая плитка позволяет купить ИЛИ продать один и тот же предмет, в зависимости от того, какой кнопкой мыши кликнули. Цена покупки выше цены продажи (торговец берёт наценку). Демонстрирует разделение по типу клика в одном и том же слоте. + + + +## Что показывает + +- `click { left { ... } right { ... } }` для разного поведения по типу кнопки +- Связку правила `money` с действием `takeMoney` для покупки +- Связку правила `inventoryItems` с действиями `itemRemove` + `giveMoney` для продажи +- Несимметричное ценообразование (купить за 20, продать за 15) + +## Как работают типы кликов + +Блок `click {}` может содержать несколько подблоков, по одному на тип кнопки мыши: + +```hocon +click { + left { rules { money: 20 }, actions { takeMoney: 20, itemAdd { ... } } } + right { rules { inventoryItems: [...] }, actions { itemRemove: [...], giveMoney: 15 } } +} +``` + +При левом клике игрока выполняется только подблок `left`. При правом - только `right`. Это отличается от плоского `click { rules: ..., actions: ... }`, который срабатывает на любой тип клика. + +Доступные типы кликов: `left`, `right`, `shift_left`, `shift_right`, `drop`. Добавляйте больше подблоков для большего числа кнопок - типичный паттерн: `left` под основное действие, `shift_left` под "сделать x8" или "продвинутый вариант". + +## Зачем несимметричные цены + +Плагины-торговцы обычно покупают предметы у игроков дешевле, чем продают. Разница (купить 20 - продать 15 = 5 наценки) - это доля торговца. Не даёт игрокам бесконечно прокручивать предметы через магазин ради прибыли. + +Чтобы магазин был защищён от арбитража: + +- buy_price > sell_price (всегда) +- buy_price > sell_price для любой цепочки и через другие магазины тоже + +Если хотите "честную цену" в UI без наценки - ставьте обе одинаковыми. + +## Проверка инвентаря при продаже + +Обработчик правого клика использует правило `inventoryItems`, чтобы проверить, есть ли у игрока предмет на продажу: + +```hocon +right { + rules { inventoryItems: [{ material: WHEAT, count: 16 }] } + actions { + itemRemove: [{ material: WHEAT, count: 16 }] + giveMoney: 15 + } +} +``` + +Если у игрока нет 16 пшеницы - правило не проходит, действия не срабатывают, и вместо них локальный `denyActions` показывает сообщение "У вас недостаточно для продажи". + +## Попробовать + +1. Закиньте бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. `/eco give <вы> 1000`. +4. Введите `/ame_buysell` в игре. +5. Левый клик по пшенице - купить. Правый - продать обратно. diff --git a/src/content/docs/ru/examples/snippets/action-bulk-delay.mdx b/src/content/docs/ru/examples/snippets/action-bulk-delay.mdx new file mode 100644 index 0000000..a330706 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/action-bulk-delay.mdx @@ -0,0 +1,40 @@ +--- +title: "Действия: bulk + delay" +description: Две обёртки для действий - bulk последовательно повторяет группы, delay откладывает действия на N тиков. +sidebar: + order: 7 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`bulk` и `delay` - это мета-действия, оборачивающие другие действия. `bulk` запускает каждую запись из списка как отдельную группу действий (так можно повторять одноимённые действия). `delay` ждёт N тиков перед запуском вложенного блока. + + + +## bulk + +```hocon +bulk: [ + { message: "&e3..." } + { message: "&62..." } + { message: "&c1..." } +] +``` + +Каждый элемент списка - самостоятельная группа действий (форма та же, что у родительского `actions {}`). Все элементы выполняются по порядку, мгновенно, в один и тот же тик. Удобно, когда нужно три сообщения подряд (нельзя положить три ключа `message:` на один уровень внутри actions - в HOCON ключи объекта должны быть уникальны). + +## delay + +```hocon +delay { + delay: 60 + actions { + sound: ${successSound} + message: "&aBoom!" + } +} +``` + +Внешний `delay:` задан в тиках (60 = 3 секунды). После задержки запускается внутренний `actions {}` как обычная группа действий. Подходит для обратных отсчётов, отложенных наград, постановочных эффектов. + +Внутри `delay` тоже можно задавать rules и denyActions - отложенный блок это полноценный самостоятельный контекст действий. diff --git a/src/content/docs/ru/examples/snippets/action-give-item.mdx b/src/content/docs/ru/examples/snippets/action-give-item.mdx new file mode 100644 index 0000000..c161399 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/action-give-item.mdx @@ -0,0 +1,37 @@ +--- +title: "Действие: itemAdd" +description: Выдать игроку один или несколько предметов. Поддерживает имя, лор, чары и любые другие поля предмета. +sidebar: + order: 6 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`itemAdd` добавляет предмет (или список предметов) в инвентарь игрока. Внутри принимается полная спецификация предмета - всё, что работает на обычном предмете меню, работает и здесь: material, name, lore, enchantments, кастомный NBT. + + + +## Как использовать + +```hocon +actions { + itemAdd { + material: WOODEN_PICKAXE + name: "&aStarter Pickaxe" + } +} +``` + +Несколько предметов через список: + +```hocon +actions { + itemAdd: [ + { material: BREAD, count: 16 } + { material: APPLE, count: 4 } + { material: GOLDEN_APPLE } + ] +} +``` + +Если инвентарь игрока полон, лишние предметы выпадают на землю (стандартное поведение). Чтобы заранее проверить место в инвентаре, ограничьте клик правилом `freeSlot` или `freeSlotCount`. diff --git a/src/content/docs/ru/examples/snippets/action-rand-actions.mdx b/src/content/docs/ru/examples/snippets/action-rand-actions.mdx new file mode 100644 index 0000000..490b285 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/action-rand-actions.mdx @@ -0,0 +1,37 @@ +--- +title: "Действие: randActions" +description: Случайно выбрать одну запись из списка групп действий. Каждая запись независима. +sidebar: + order: 8 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`randActions` принимает список групп действий и случайно выбирает ровно одну для выполнения. Каждая запись - самостоятельная группа: звуки, сообщения, предметы, что угодно. + + + +## Как использовать + +```hocon +randActions: [ + { message: "Outcome A" } + { message: "Outcome B" } + { message: "Outcome C" } +] +``` + +По умолчанию у каждой записи равная вероятность. Чтобы **взвесить** исходы, повторяйте их - запись, появившаяся дважды, имеет вдвое больше шансов: + +```hocon +randActions: [ + { message: "Common outcome" } + { message: "Common outcome" } + { message: "Common outcome" } + { message: "Rare outcome" } +] +``` + +Сверху: 3 шанса из 4 на "Common", 1 из 4 на "Rare". + +Для одного из N в отображении (какой предмет показать), а не одного из N в действиях, используйте правило `chance` на предметах - смотрите [Правило: chance](./rule-chance). diff --git a/src/content/docs/ru/examples/snippets/activator-clickitem.mdx b/src/content/docs/ru/examples/snippets/activator-clickitem.mdx new file mode 100644 index 0000000..208faf0 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/activator-clickitem.mdx @@ -0,0 +1,51 @@ +--- +title: "Активатор: clickItem" +description: Открывать меню правым кликом по конкретному предмету в руке. Без команды. +sidebar: + order: 9 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickItem` открывает меню, когда игрок правым кликом активирует предмет, подходящий под спецификацию, держа его в руке. Подходит для интерфейсов "магического жезла", именных инструментов с открытием меню, навигации через компас и т.п. + + + +## Как использовать + +```hocon +activators { + clickItem { + material: NETHER_STAR + name: "&6Menu Wand" + } +} +``` + +Активатор сработает, только если совпадают все указанные свойства. Пример выше требует и материал, И точное имя предмета. Если игрок кликнет правой кнопкой по NETHER_STAR с другим именем - ничего не произойдёт. + +Для совпадения только по материалу уберите поле `name:`. Для нескольких триггеров используйте список: + +```hocon +activators { + clickItem: [ + { material: COMPASS } + { material: NETHER_STAR, name: "&6Menu Wand" } + ] +} +``` + +Плагин выставляет внутри меню плейсхолдеры `%activator_item_*%`, открывающие данные кликнутого предмета. Полную таблицу экстракторов смотрите в [Контексте активатора](/docs/ru/general/activators/#activator-context-and-placeholders). + +## Другие активаторы "от мира" + +Та же форма, разные источники: + +- `clickBlock` - клик по конкретному блоку с известными координатами +- `clickBlockType` - клик по любому блоку заданного материала +- `clickEntity` / `shiftClickEntity` - клик по сущности (мобу, стенду брони, рамке) +- `clickNPC` - клик по NPC из Citizens по id +- `regionJoin` / `regionLeave` - триггеры регионов WorldGuard +- `button` / `lever` / `plate` - стандартные редстоун-устройства + +Их можно комбинировать в одном блоке `activators {}` - меню можно открыть и командой, и кликом по предмету, и входом в регион, всё одновременно. diff --git a/src/content/docs/ru/examples/snippets/index.mdx b/src/content/docs/ru/examples/snippets/index.mdx new file mode 100644 index 0000000..842cf41 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/index.mdx @@ -0,0 +1,18 @@ +--- +title: Сниппеты +description: Маленькие демо одной фичи на каждое правило, действие и активатор. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Маленькие демо одной фичи каждое: по одному правилу, действию и активатору. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/snippets/inventory-crafting.mdx b/src/content/docs/ru/examples/snippets/inventory-crafting.mdx new file mode 100644 index 0000000..055f9c7 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/inventory-crafting.mdx @@ -0,0 +1,79 @@ +--- +title: Инвентарный крафт +description: Крафт предметов с расходом ингредиентов из инвентаря игрока. Демонстрирует правило inventoryItems в связке с действиями itemRemove + itemAdd. +sidebar: + order: 11 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Меню в стиле верстака, которое "крафтит" предметы: игрок должен иметь ингредиенты рецепта в инвентаре, клик по рецепту их расходует и выдаёт результат. Механика отличается от слотов с drag-and-drop - здесь проверяется обычный инвентарь игрока. + +## Что показывает + +- Правило `inventoryItems` для проверки "есть ли у игрока эти предметы" +- `itemRemove` для расхода подходящих предметов +- `itemAdd` для выдачи результата крафта +- Объявление рецептов как именованных блоков на уровне файла, на которые ссылаются через подстановку `${name}` + + + +## Как это работает + +Каждый рецепт описывает `output` (полученный предмет) и список `requirements` (ингредиенты): + +```hocon +helmetRecipe { + output { + material: LEATHER_HELMET + name: "&aHunter's Hat" + enchantments { durability: 2 } + } + requirements: [ + { material: LEATHER, count: 5 } + ] +} +``` + +Блок click ссылается на оба: + +```hocon +click { + rules { inventoryItems: ${helmetRecipe.requirements} } + actions { + itemRemove: ${helmetRecipe.requirements} + itemAdd: ${helmetRecipe.output} + ... + } + denyActions: ${denyNotEnoughItems} +} +``` + +Этот паттерн держит данные рецепта в одном месте: правило, расход и результат - всё ссылается на один и тот же блок `helmetRecipe`. Добавить или поправить рецепт значит править один блок, а не три. + +## Инвентарный крафт vs drag-and-drop + +Два разных паттерна "крафта" в AbstractMenus: + +- **Инвентарный крафт** (этот пример): предметы остаются в обычном инвентаре игрока. Правило `inventoryItems` смотрит в инвентарь, `itemRemove` списывает оттуда. Игрок ничего не перемещает руками. +- **Drag-and-drop крафт**: в меню есть слоты, в которые можно перетаскивать. Игрок физически перетягивает предметы. Использует действие `placeItem`, правило `placedItem` и поле `draggable` у предметов. Альтернативный паттерн смотрите в [документации drag-and-drop](/docs/ru/advanced/drag-and-drop/). + +Этот сниппет показывает паттерн инвентарного крафта, потому что он хорошо сочетается с магазинными интерфейсами и не требует кастомной настройки слотов. + +## Добавление новых рецептов + +Опишите новый блок `xxxRecipe` на уровне файла с `output {}` и `requirements: [...]`. Добавьте предмет в список `items`, ссылающийся на него по той же схеме, что и шлем. Рецепт упоминается в трёх местах: предмет в отображении, правило, действия. Если везде стоит `${xxxRecipe.something}`, рецепты не разъедутся. + +## Попробовать + +После установки набора примеров: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Получите 5 кожи в инвентарь. +4. Введите в игре `/ame_craft`. +5. Кликните по рецепту шлема. Кожа расходуется, шапка появляется. diff --git a/src/content/docs/ru/examples/snippets/rule-and-or.mdx b/src/content/docs/ru/examples/snippets/rule-and-or.mdx new file mode 100644 index 0000000..4c4bdcb --- /dev/null +++ b/src/content/docs/ru/examples/snippets/rule-and-or.mdx @@ -0,0 +1,48 @@ +--- +title: "Правила: and / or" +description: Логические обёртки для комбинирования правил. and = все совпадают. or = хотя бы одно совпадает. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Стандартный блок `rules { ... }` уже объединяет всё внутри по AND. Явная обёртка `and` нужна в основном внутри `or` (потому что иначе AND внутри OR не вложить). Обёртка `or` проходит, когда срабатывает любое из вложенных правил. + + + +## Как использовать + +```hocon +rules { + or { + permission: "vip" + money: 1000 + } +} +``` + +Проходит, если у игрока есть право VIP **ИЛИ** $1000+. Достаточно любого из условий. + +```hocon +rules { + or: [ + { + and { + permission: "vip" + money: 100 + } + } + { + and { + permission: "admin" + gamemode: CREATIVE + } + } + ] +} +``` + +Вложенная форма: проходит, если (VIP и $100+) ИЛИ (админ и креативный режим). Каждый элемент списка `or:` - отдельная группа правил. + +Есть ещё `oneof` (срабатывает на первой истинной записи, прерывая дальнейшие проверки) - смотрите пример [Item Enhancer](../state-and-vars/item-enhancer). diff --git a/src/content/docs/ru/examples/snippets/rule-chance.mdx b/src/content/docs/ru/examples/snippets/rule-chance.mdx new file mode 100644 index 0000000..56ce908 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/rule-chance.mdx @@ -0,0 +1,27 @@ +--- +title: "Правило: chance" +description: Вероятностное отображение - предмет появляется только N% случаев при открытии меню. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`chance: 30` означает "33 раза из 100 это правило проходит". Подходит для редких лутов, лотерейных билетов и любых "иногда виден X" интерфейсов. + + + +## Как использовать + +```hocon +{ + slot: 4 + material: NETHER_STAR + name: "&dRare item" + rules { chance: 30 } +} +``` + +Бросок происходит при отрисовке меню (на открытие и на каждый тик `updateInterval`, если он задан). Чтобы получить запасной предмет, который виден в остальных 70% случаев, добавьте второй предмет в тот же слот без правил - такой паттерн с двумя предметами всё решит. + +Для случайных взвешенных исходов после клика (а не отображения) используйте `randActions` - смотрите [Действие: randActions](./action-rand-actions). diff --git a/src/content/docs/ru/examples/snippets/rule-if-placeholder.mdx b/src/content/docs/ru/examples/snippets/rule-if-placeholder.mdx new file mode 100644 index 0000000..a4b38a5 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/rule-if-placeholder.mdx @@ -0,0 +1,30 @@ +--- +title: "Правило: if (выражение с плейсхолдером)" +description: Сравнивать значения плейсхолдеров операторами вроде >=, ==, ||, &&. Универсальное условное правило. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Когда встроенного правила вроде `permission` или `money` не хватает, правило `if` позволяет писать выражения над любым плейсхолдером. Поддерживаются `>`, `<`, `>=`, `<=`, `==`, `!=`, `===`, `!==`, `&&`, `||`, плюс скобки. + + + +## Как использовать + +```hocon +rules { + if: "%player_level% >= 10" +} +``` + +```hocon +rules { + if: "(%player_lvl% == 5 || %player_lvl% == 10) && %player_world% == survival" +} +``` + +Внутри выражения плейсхолдеры разворачиваются в строковые или числовые значения. Голые строки (без кавычек) сравниваются операторами. Используйте `===`/`!==` для регистронезависимого сравнения имён. + +Для сложной математики используйте правило `js` - оно выполняет JavaScript-выражения. `if` работает быстрее и подходит для простой логики. diff --git a/src/content/docs/ru/examples/snippets/rule-money.mdx b/src/content/docs/ru/examples/snippets/rule-money.mdx new file mode 100644 index 0000000..d7ea3e3 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/rule-money.mdx @@ -0,0 +1,29 @@ +--- +title: "Правило: money" +description: Ограничить клик наличием валюты у игрока. В паре с takeMoney - чтобы реально списать деньги. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Правило `money` спрашивает у настроенного провайдера экономики: "есть ли у игрока хотя бы N?". Если да - выполняются действия клика; если нет - выполняется `denyActions`. + + + +## Как использовать + +```hocon +click { + rules { money: 50 } + actions { + takeMoney: 50 + itemAdd { ... } + } + denyActions: ${denyNoMoney} +} +``` + +Правило только проверяет баланс - оно не списывает. Списанием занимается действие `takeMoney`. Такое разделение позволяет использовать `money` как чисто визуальный ограничитель (показывать разные предметы в зависимости от баланса) без расхода монет игрока. + +Используйте форму объекта `money: { amount: 50, provider: "vault" }`, чтобы привязать проверку к конкретному провайдеру экономики, когда зарегистрировано несколько. diff --git a/src/content/docs/ru/examples/snippets/rule-permission.mdx b/src/content/docs/ru/examples/snippets/rule-permission.mdx new file mode 100644 index 0000000..aa1f5e9 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/rule-permission.mdx @@ -0,0 +1,24 @@ +--- +title: "Правило: permission" +description: Ограничить клик одним правом. Минимальное демо правила permission. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Правило permission - самый частый ограничитель в любом меню: "разреши игроку это, только если у него есть право". В паре с `denyActions` даёт дружелюбный отказ. + + + +## Как использовать + +```hocon +click { + rules { permission: "myplugin.admin" } + actions { ... } + denyActions: ${denyNoPerm} +} +``` + +Правило проходит, если у игрока есть указанное право. Чтобы ограничить по группе, а не по правилу, переключайтесь на правило `group` (работает с LuckPerms). Чтобы инвертировать (разрешить только тем, у кого права НЕТ), добавьте префикс `-`: `-permission: "..."`. diff --git a/src/content/docs/ru/examples/snippets/template-substitution.mdx b/src/content/docs/ru/examples/snippets/template-substitution.mdx new file mode 100644 index 0000000..6a1c774 --- /dev/null +++ b/src/content/docs/ru/examples/snippets/template-substitution.mdx @@ -0,0 +1,54 @@ +--- +title: "Подстановка шаблона HOCON" +description: Опишите форму предмета один раз, инстанцируйте многократно с переопределением каждого использования. Главная фича HOCON. +sidebar: + order: 10 +--- + +import MenuExample from '@components/MenuExample.astro'; + +HOCON поддерживает подстановку `${name}` - ссылку на ранее определённый блок по имени. Если за ней идёт `{ ... переопределения ... }`, переопределение мержится поверх подставленного значения. Именно за счёт этого работают общий `_shared/templates.conf` и локальные шаблоны в каждом меню. + + + +## Как использовать + +```hocon +# Описать один раз +prizeTile { + material: NETHER_STAR + lore: ["&7Click for a prize."] + click { actions { itemAdd { material: DIAMOND } } } +} + +# Использовать много раз с переопределениями +items: [ + ${prizeTile} { slot: 2, name: "&aCommon Prize" } + ${prizeTile} { slot: 4, name: "&6Rare Prize" } + ${prizeTile} { slot: 6, name: "&dEpic Prize" } +] +``` + +Каждый экземпляр стартует как копия `prizeTile` (material, lore, click), затем блок переопределения добавляет slot и name. На выходе три разных предмета с общей структурой, отличающиеся только идентифицирующими полями. + +## Семантика мержа + +`${A} { B }` делает **глубокий мерж объектов** - ключи из B перебивают ключи из A, но вложенные объекты мержатся рекурсивно. Если у A есть `click.actions.sound: ${clickSound}`, а у B есть `click.actions.message: "Hi"`, в результате будет и то и другое: `click.actions.{sound: ..., message: "Hi"}`. + +Для списков переопределение ЗАМЕНЯЕТ исходный список - не добавляет в конец. Чтобы расширить список lore: + +```hocon +${prizeTile} { + lore: ${prizeTile.lore} [ "&7Extra line" ] +} +``` + +Подстановка `${prizeTile.lore}` подтягивает исходный массив lore, а `[...]` добавляет к нему новые записи. Этот паттерн в действии смотрите в примере [Daily Kit](../kits-and-rewards/daily-kit-cooldown). + +## Когда выносить в шаблон + +- Форма предмета повторяется 3+ раза - выносите в шаблон +- Один обработчик клика используется в нескольких предметах - вынесите `clickFoo {...}` и подставляйте через `click: ${clickFoo}` +- Стоимость или цена общая для отображаемого предмета и проверки правила - опишите один раз на уровне файла, ссылайтесь в обоих местах + +Если что-то используется дважды и встроенный код по-прежнему читается понятно, оставьте как есть. Шаблоны окупаются, когда они уменьшают площадь правок при изменении повторяющейся логики. diff --git a/src/content/docs/ru/examples/state-and-vars/bungee-status.mdx b/src/content/docs/ru/examples/state-and-vars/bungee-status.mdx new file mode 100644 index 0000000..413e188 --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/bungee-status.mdx @@ -0,0 +1,67 @@ +--- +title: Bungee Status +description: Выбор сервера для сетей на BungeeCord/Velocity. Каждая плитка показывает живой онлайн и подключает игрока по клику. +sidebar: + order: 4 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Сети на BungeeCord или Velocity отдают онлайн по серверам через plugin messages. AbstractMenus прокидывает их как плейсхолдеры `%bungee_%`. Это меню использует их, чтобы собрать "выбор сервера" с живым онлайном и плитками "клик - подключиться". + +## Что показывает + +- Действие `bungeeConnect` для перехода между серверами +- Плейсхолдер `%bungee_%` для живого онлайна +- Использование `glow: true`, чтобы выделить главную плитку (в нашем случае - лобби) +- Связку `updateInterval` с bungee-плейсхолдерами, чтобы онлайн оставался свежим + + + +## Как работает + +Каждая плитка - обычный предмет с одним кастомным действием: `click { bungeeConnect: "lobby" }`. По клику плагин шлёт BungeeCord-сообщение о переходе, и прокси отправляет игрока на нужный бэкенд по имени. + +Плейсхолдер `%bungee_lobby%` спрашивает у BungeeCord-листенера "сколько игроков прямо сейчас на бэкенде `lobby`?" и рендерит число. С `updateInterval: 40` (2 секунды) онлайн обновляется, пока меню открыто. + +Чтобы счётчик работал: + +1. На сервере с AbstractMenus должно быть `bungeeCord: true` в `plugins/AbstractMenus/config.conf`. +2. Прокси должен знать то же самое имя сервера в своём конфиге (`lobby` в `bungeeConnect: "lobby"` должно совпадать с зарегистрированным именем на прокси). +3. BungeeCord plugin messaging должен быть включён с обеих сторон. + +## Добавить ещё серверов + +Каждая плитка независима. Скопируйте любой блок, поменяйте слот, имя, lore и две ссылки на имя сервера (`bungeeConnect` + плейсхолдер). Реальные имена бэкендов вместо `lobby` / `survival` и т.д. + +Для разнообразия меняйте `material:` под режим (TNT для "TNT Games", IRON_SWORD для "Battle Royale" и т.п.). + +## Показывать online/offline статус + +Чтобы зашейпить плитку по доступности сервера, используйте правило `bungeeIsOnline`: + +```hocon +{ + slot: 10 + ... + rules { + bungeeIsOnline: "lobby" + } +} +``` + +Если лобби оффлайн, предмет не отрендерится. Добавьте на тот же слот фолбек "offline" без правил с другим внешним видом (серая краска, текст "Сервер оффлайн") - получите паттерн dual-item из примера [Счётчик и тоггл](./counter-toggle). + +## Попробовать + +После установки example pack: + +1. Поднимите BungeeCord/Velocity-прокси с бэкендами по именам `lobby`, `survival`, `sw`, `bb`. +2. Поставьте `bungeeCord: true` в `plugins/AbstractMenus/config.conf`. +3. Положите бандл в `plugins/AbstractMenus/menus/example/`. +4. `/am reload`. +5. Введите `/ame_servers` в игре. diff --git a/src/content/docs/ru/examples/state-and-vars/counter-toggle.mdx b/src/content/docs/ru/examples/state-and-vars/counter-toggle.mdx new file mode 100644 index 0000000..38a67d2 --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/counter-toggle.mdx @@ -0,0 +1,74 @@ +--- +title: Счётчик и тоггл +description: Персональные переменные в действии - кликабельный счётчик и тоггл с состоянием, оба сохраняются между сессиями. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Два самых частых примитива состояния в любом GUI: числовой счётчик, который можно увеличивать и уменьшать, и бинарный тоггл. Оба показывают систему персональных переменных (`varp`) и паттерн dual-item для условного отображения. + +## Что показывает + +- `incVarp` / `decVarp` / `removeVarp` для числовых мутаций +- `setVarp` с шорткатом `"name::value"` +- Синтаксис плейсхолдера `%varp_:name:default%` +- Правило `existVarp` для проверки "существует ли эта переменная у игрока" +- Паттерн dual-item: два предмета на одном слоте, у первого правила, второй - фолбек +- `refreshMenu: true`, чтобы отображение обновлялось без закрытия и переоткрытия меню + + + +## Как работает счётчик + +Счётчик использует три действия и плейсхолдер: + +- `incVarp: "ame_counter"` прибавляет 1 к переменной. Если её нет, плагин сначала автоматически инициализирует её нулём. +- `decVarp: "ame_counter"` отнимает 1. +- `removeVarp: "ame_counter"` удаляет переменную - фактически сброс в "счётчика нет". + +Отображающий предмет показывает `%varp_:ame_counter:0%`. Суффикс `:0` - значение по умолчанию: когда переменная не существует, плейсхолдер рендерится как `0`, а не пустая строка. + +Каждое click-действие заканчивается на `refreshMenu: true`, чтобы отображение обновлялось сразу. Без этого счётчик обновлялся бы только при срабатывании auto-update интервала меню (или никогда, если `updateInterval` не задан). + +## Как работает тоггл + +Тоггл использует **паттерн dual-item**: два предмета объявлены на одном слоте, первый рендерится только если его правила проходят, а второй - фолбек. + +```hocon +# Виден, когда тоггл задан +{ slot: 15, material: LIME_DYE, rules { existVarp: "ame_toggle" }, ... } + +# Виден, когда тоггл НЕ задан +{ slot: 15, material: GRAY_DYE, ... } # без правил = всегда проходит +``` + +Когда у игрока задана переменная `ame_toggle`, показывается зелёный предмет, и клик удаляет переменную. Когда переменной нет, показывается серый предмет, и клик ставит переменную в "1" (значение неважно для проверки существования, важно лишь, что переменная есть). + +Этот паттерн часто используют для вещей вроде "выдать предмет / уже забрано", "VIP-цена / обычная цена", "замьючен / размьючен" - любое бинарное состояние, где игрок должен видеть разную картинку. + +## Кастомизация + +Чтобы поменять имя переменной, замените `ame_counter` и `ame_toggle` везде - в действиях, в правиле `existVarp` и в плейсхолдере. Берите имена с серверным префиксом, чтобы избежать коллизий с другими меню. + +Для счётчика с шагом больше 1 используйте объектную форму: + +```hocon +incVarp: [ + { name: "ame_counter", amount: 5 } +] +``` + +## Попробовать + +После установки example pack: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_counter` в игре. +4. Покликайте. Счётчик сохраняется - закройте меню, откройте снова, значение на месте. diff --git a/src/content/docs/ru/examples/state-and-vars/global-stats-board.mdx b/src/content/docs/ru/examples/state-and-vars/global-stats-board.mdx new file mode 100644 index 0000000..5e6d91d --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/global-stats-board.mdx @@ -0,0 +1,56 @@ +--- +title: Доска статов +description: Дашборд с живым обновлением, где видны статистика игрока и состояние сервера. Показывает плейсхолдеры + обновление через updateInterval. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Read-only меню с живыми данными через PlaceholderAPI. Верхний ряд - персональные статы (убийства, смерти, часы в игре), нижний - сервер целиком (онлайн, время, TPS, текущий мир). Значения обновляются раз в секунду благодаря `updateInterval: 20` (20 тиков = 1 секунда). + +## Что показывает + +- Поле `updateInterval` для живого обновления меню +- Интеграция с PlaceholderAPI: `%statistic_*%`, `%server_*%`, `%player_*%` +- Смешение персональных и серверных данных в одном меню +- Кастомные текстуры голов через поле `texture:` на PLAYER_HEAD + + + +## Как работает updateInterval + +`updateInterval: 20` говорит меню перерасчитывать плейсхолдеры каждые 20 серверных тиков (1 секунда). На каждом тике у каждого предмета `name:` и `lore:` рендерятся заново с актуальными значениями плейсхолдеров. Игроки видят, как счётчики растут в реальном времени, без закрытия и переоткрытия. + +Для статичных меню это поле не нужно - они отрисовываются один раз при открытии. Для дашбордов с постоянно меняющимися значениями (статы, онлайн) - обязательно. + +Поставите слишком низко (например, `1` = каждый тик = 50мс) - получите всплески нагрузки на CPU от вычисления плейсхолдеров. `20` (1с) или `40` (2с) - золотая середина для читаемого человеком обновления. + +## Требования по плейсхолдерам + +Меню использует плейсхолдеры PlaceholderAPI из нескольких расширений: + +- **`%statistic_*%`** - [Statistic expansion](https://api.extendedclip.com/expansions/statistic/) для PlaceholderAPI. Установка: `/papi ecloud download Statistic`. +- **`%server_*%`** - встроенные серверные плейсхолдеры PlaceholderAPI. Доп. расширения не нужны. +- **`%player_*%`** - встроенные игровые плейсхолдеры PlaceholderAPI. Доп. расширения не нужны. + +Если плейсхолдер не разрешился, в меню он рендерится как буквальная строка `%xxx%` - это знак, что расширение не установлено. + +## Добавить ещё статов + +Возьмите плейсхолдер из [облака PlaceholderAPI](https://api.extendedclip.com/all/) и впишите в `lore` нового предмета. Меню подхватит живое значение на следующем обновлении. + +Для персональных переменных самого плагина (не PAPI) используйте форму `%varp_:name:default%` - см. пример [Счётчик и тоггл](./counter-toggle). + +## Попробовать + +После установки example pack: + +1. Установите [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) и Statistic expansion (`/papi ecloud download Statistic`). +2. Положите бандл в `plugins/AbstractMenus/menus/example/`. +3. `/am reload`. +4. Введите `/ame_stats` в игре. Смотрите, как значения обновляются раз в секунду. diff --git a/src/content/docs/ru/examples/state-and-vars/index.mdx b/src/content/docs/ru/examples/state-and-vars/index.mdx new file mode 100644 index 0000000..0c11893 --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/index.mdx @@ -0,0 +1,18 @@ +--- +title: Состояние и переменные +description: Счётчики, кулдауны голосования, live-доска статов через personal и global vars. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Счётчики, кулдауны на голосование, live-доска статов через personal и global переменные. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/state-and-vars/item-enhancer.mdx b/src/content/docs/ru/examples/state-and-vars/item-enhancer.mdx new file mode 100644 index 0000000..467b1a9 --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/item-enhancer.mdx @@ -0,0 +1,65 @@ +--- +title: Энхансер предметов +description: Кладёшь ванильный предмет, получаешь улучшенный. Показывает drag-and-drop слоты в связке с правилом oneof для условных трансформаций. +sidebar: + order: 5 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Меню энхансера с фиксированной таблицей: камень становится угольной рудой, яблоко - золотым яблоком, деревянный меч - алмазным мечом, деревянный топор - алмазным топором. Построено вокруг правила `oneof`, которое короткозамыкается на первом совпадении - идеально подходит под "переключиться по типу предмета и сделать своё". + +## Что показывает + +- Тип правила `oneof` и его семантику "первое совпадение выигрывает" +- Действия per-ветке внутри записей oneof (у каждой записи свой `actions {}`) +- Drag-and-drop слоты и правило `placedItem` для матчинга входа +- Сборку drag-and-drop примитивов (`placeItem`, `removePlaced`, `setButton`) в пайплайн трансформации +- Шаблоны для переиспользуемых правил и действий (`${inputSlotRule}`, `${outputAction}`) + + + +## Почему state-and-vars? + +Этот пример лежит в state-and-vars, потому что drag-and-drop сама по себе - форма состояния меню: предметы, физически лежащие в слотах 2 и 6, это сессионные "переменные" меню. `setVar`/`setVarp` тут не используются, но концептуальный паттерн (читать состояние слота, ветвиться по нему, мутировать его) тот же. + +## Как работает `oneof` + +`oneof` - логическая обёртка-правило, принимающая список записей. Она проходит список сверху вниз и останавливается на первой, чьё собственное правило совпадает. Блок `actions {}` совпавшей записи и выполняется. + +```hocon +oneof: [ + { placedItem: { slot: 2, material: STONE }, actions { placeItem: { ..., material: COAL_ORE } } } + { placedItem: { slot: 2, material: APPLE }, actions { placeItem: { ..., material: GOLDEN_APPLE } } } + ... +] +``` + +Если ни одна запись не совпала, весь `oneof` возвращает false. Внешний click тогда выполняет `denyActions` вместо успешной ветки. + +Чище, чем вложенные `if`/`and`/`or`, для switch-подобной логики. + +## Добавить новый рецепт + +Допишите в список `oneof` внутри `enhanceRules`: + +```hocon +{ placedItem: ${inputSlotRule} { material: COAL } + actions { placeItem: ${outputAction} { material: DIAMOND } } } +``` + +Подстановки `${inputSlotRule}` и `${outputAction}` держат номера слотов в одном месте - если переразметите меню, поменяются только два якорных блока, а не каждая запись рецепта. + +## Попробовать + +После установки example pack: + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_enhancer` в игре. +4. Перетащите блок камня в левый слот. +5. Кликните "Улучшить!". В правом слоте появится угольная руда. diff --git a/src/content/docs/ru/examples/state-and-vars/vote-cooldown.mdx b/src/content/docs/ru/examples/state-and-vars/vote-cooldown.mdx new file mode 100644 index 0000000..b8c24fb --- /dev/null +++ b/src/content/docs/ru/examples/state-and-vars/vote-cooldown.mdx @@ -0,0 +1,56 @@ +--- +title: Кулдаун на голосование +description: Кнопка ежедневного голосования - глобальный счётчик плюс персональный 24-часовой кулдаун через временные переменные. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Меню голосования с двумя кусками состояния: глобальный счётчик, который инкрементят все (общее количество голосов сервера за всё время), и персональная временная переменная, которая блокирует игрока на 24 часа после голоса. Связка `incVar` (глобальный) с `setVarp ::1d` (персональный временный) - типичный паттерн "голосуй раз в сутки". + + + +## Что показывает + +- `incVar` для глобальных счётчиков, общих для всех игроков +- `setVarp` с временным суффиксом `::1d` +- Плейсхолдер `%var_:name:default%` для глобальных переменных (синтаксис двоеточия и дефолта тот же, что у varp) +- Связка глобальных и персональных переменных в одном click-обработчике +- Dual-item для кнопки голосования (состояния ON_COOLDOWN и READY) + +## Глобальные vs персональные + +Два семейства переменных: + +- **`var_*` / setVar / incVar** - глобальные, на весь сервер. Все читают и пишут одно и то же значение. Здесь - для общего количества голосов. +- **`varp_*` / setVarp / incVarp** - персональные. У каждого игрока своя копия. Здесь - для блокировки кулдауна. + +При клике трогаются оба внутри одного блока actions: + +```hocon +actions { + incVar: "ame_vote_total" # глобальный счётчик +1 + setVarp: "ame_vote_cd::1::1d" # персональная блокировка на 24 часа + ... +} +``` + +Глобальный счётчик растёт бесконечно. Персональный кулдаун сам истекает через 1 день, открывая кнопку голосования заново. + +## Интеграция с реальным голосованием + +Этот пример - самостоятельная демка. Чтобы привязать к настоящему голосованию (Votifier, MinecraftPocket и т.д.): + +1. Не давайте игроку кликать по кнопке голосования - награда триггерится внешним vote-сайтом/прокси. +2. Используйте ту же связку `incVar` + `setVarp` внутри Bukkit-листенера, который обрабатывает входящие голоса. +3. Поставьте перму вроде `iconmenu.canclaim` или другой гейт, и это меню превратится в "забрать награду за голос", а не в "проголосовать". + +В разделе документации про переменные описаны опции персистентности, если нужны гарантии после рестарта на глобальном счётчике. + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Введите `/ame_vote` в игре. +4. Кликните на лаймовую кнопку "Голосовать!". Счётчик растёт, кнопка становится серой с обратным отсчётом 23ч 59м. diff --git a/src/content/docs/ru/examples/world-integrations/block-as-button.mdx b/src/content/docs/ru/examples/world-integrations/block-as-button.mdx new file mode 100644 index 0000000..92042a5 --- /dev/null +++ b/src/content/docs/ru/examples/world-integrations/block-as-button.mdx @@ -0,0 +1,57 @@ +--- +title: Блок как кнопка +description: Правый клик по любому блоку выбранного материала открывает меню. Активатор clickBlockType + плейсхолдеры контекста блока. +sidebar: + order: 3 +--- + +import MenuExample from '@components/MenuExample.astro'; + +`clickBlockType` слушает правые клики по любому блоку заданного материала, в любой точке мира. Отличается от `clickBlock` (который привязан к конкретному блоку по координатам). Открывшееся меню может прочитать позицию, мир и тип кликнутого блока через плейсхолдеры `%activator_block_*%`. + + + +## Что показывает пример + +- Активатор `clickBlockType`, нацеленный на материал +- Чтение контекста клика через `%activator_block_x%`, `%activator_block_y%`, `%activator_block_z%`, `%activator_block_world%` +- Размещение плейсхолдеров в `title:` меню для динамических заголовков под каждый клик + +## Формат активатора + +```hocon +activators { + clickBlockType: ["DIAMOND_BLOCK"] +} +``` + +Список материалов. Меню открывается, когда игрок кликает правой кнопкой по любому блоку, чей тип есть в списке. Чтобы добавить триггеров: + +```hocon +activators { + clickBlockType: ["DIAMOND_BLOCK", "EMERALD_BLOCK", "BEACON"] +} +``` + +Учтите, что это срабатывает на КАЖДОМ блоке указанных типов - в пределах всего сервера. Для сценария "конкретный блок по известным координатам" (например, один указатель на спавне) используйте `clickBlock` с миром и координатами. + +## Плейсхолдеры контекста блока + +Внутри меню `%activator_block_*%` отдают данные кликнутого блока: + +| Плейсхолдер | Возвращает | +|---|---| +| `%activator_block_world%` | Имя мира | +| `%activator_block_x%` | Координата X | +| `%activator_block_y%` | Координата Y | +| `%activator_block_z%` | Координата Z | +| `%activator_block_type%` | Имя материала | + +В примере они используются в заголовке меню - клик по разным алмазным блокам открывает меню с разными координатами в заголовке. Удобно для интерфейсов "информация про этот конкретный указатель". + +## Попробовать + +1. Положите бандл в `plugins/AbstractMenus/menus/example/`. +2. `/am reload`. +3. Поставьте алмазный блок где-нибудь в мире. +4. Кликните по нему правой кнопкой. Меню откроется с координатами блока в заголовке. diff --git a/src/content/docs/ru/examples/world-integrations/index.mdx b/src/content/docs/ru/examples/world-integrations/index.mdx new file mode 100644 index 0000000..7798ea4 --- /dev/null +++ b/src/content/docs/ru/examples/world-integrations/index.mdx @@ -0,0 +1,18 @@ +--- +title: Интеграции мира +description: Меню от региона, NPC-кликов, блок-как-кнопка активаторов. +sidebar: + order: 0 +--- + +import CategoryIndex from '@components/CategoryIndex.astro'; + +Меню, открывающиеся по входу в регион, по клику на NPC, по клику на блок определённого материала. + +## Меню в этой категории + + + +## Скачать + +Используй [Pack Builder](../builder/) чтобы собрать эти меню вместе с другими в один zip. diff --git a/src/content/docs/ru/examples/world-integrations/npc-click-info.mdx b/src/content/docs/ru/examples/world-integrations/npc-click-info.mdx new file mode 100644 index 0000000..28c88b7 --- /dev/null +++ b/src/content/docs/ru/examples/world-integrations/npc-click-info.mdx @@ -0,0 +1,58 @@ +--- +title: Клик по NPC +description: Универсальное меню с информацией и действиями, открывающееся по клику на любого из нескольких Citizens NPC. Читает контекст NPC, чтобы персонализировать меню. +sidebar: + order: 2 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Если [NPC-торговец](../shops/npc-vendor) - это один конкретный NPC, открывающий один конкретный магазин, то это меню - обобщённый интерфейс "что это за NPC", который может вызвать любой из нескольких NPC. Меню читает id, имя и мир кликнутого NPC через контекстные плейсхолдеры и предлагает действия, которые работают для любого из них. + + + +## Что показывает пример + +- Перечисление нескольких ID NPC в `clickNPC: [1, 2, 3]`, чтобы любой из них открывал это меню +- Персонализацию меню через `%activator_npc_name%`, `%activator_npc_id%`, `%activator_npc_world%` в заголовке, именах предметов и lore +- Использование `skullOwner: "%activator_npc_name%"`, чтобы текстура головы в заголовке соответствовала имени кликнутого NPC +- Маршрутизацию разных NPC в разные меню (оставлено как упражнение через действие openMenu) + +## Паттерн маршрутизации NPC + +Плитка "Поговорить с торговцем" в примере открывает `ame_shop` для любого NPC. На реальном сервере можно маршрутизировать по-разному в зависимости от NPC: + +```hocon +click { + actions { + rules { + and { + # Гипотетически: только NPC #2 открывает редкий магазин + if: "%activator_npc_id% == 2" + } + actions { openMenu: "ame_rare_shop" } + denyActions { openMenu: "ame_shop" } + } + } +} +``` + +Для более чем 2 маршрутов используйте `oneof`: + +```hocon +oneof: [ + { if: "%activator_npc_id% == 1", actions { openMenu: "ame_blacksmith_shop" } } + { if: "%activator_npc_id% == 2", actions { openMenu: "ame_alchemy_shop" } } + { if: "%activator_npc_id% == 3", actions { openMenu: "ame_armorer_shop" } } +] +``` + +`oneof` останавливается на первом совпадении, так что каждый ID NPC попадает ровно в одну ветку. + +## Попробовать + +1. Установите Citizens. +2. Заспавните трёх NPC, запомните их ID. +3. Положите бандл в `plugins/AbstractMenus/menus/example/`. Замените `clickNPC: [1, 2, 3]` на ваши реальные ID NPC. +4. `/am reload`. +5. Кликните правой кнопкой по любому из NPC. Меню откроется с данными этого NPC в заголовке. diff --git a/src/content/docs/ru/examples/world-integrations/region-welcome.mdx b/src/content/docs/ru/examples/world-integrations/region-welcome.mdx new file mode 100644 index 0000000..349301f --- /dev/null +++ b/src/content/docs/ru/examples/world-integrations/region-welcome.mdx @@ -0,0 +1,61 @@ +--- +title: Приветствие региона +description: Открывает приветственное меню автоматически, когда игрок входит в регион WorldGuard. Без команды, активатор - сам регион. +sidebar: + order: 1 +--- + +import MenuExample from '@components/MenuExample.astro'; + +Распространённый паттерн серверов: игрок заходит на спавн - и ему показывают приветственный экран с быстрыми кнопками (правила, хаб, кит). Активатор тут - `regionJoin` от WorldGuard, а не команда. Меню открывается само, когда игрок пересекает границу региона. + +## Что показывает пример + +- Активатор `regionJoin` (и его собрат `regionLeave`) +- Интеграцию с WorldGuard через имена регионов +- Сочетание пассивного приветствия и кнопок быстрого перехода в другие меню + + + +## Как это работает + +Блок активатора перечисляет один или несколько ID регионов: + +```hocon +activators { + regionJoin: ["spawn"] +} +``` + +Когда игрок входит в регион `spawn` (определённый в WorldGuard), меню открывается у него. Можно перечислить несколько ID регионов; меню откроется при входе в любой из них. + +В этом меню нет правил денег и действий покупки - это в чистом виде доска объявлений. Три плитки быстрых ссылок в среднем ряду через `openMenu` направляют игрока в другие примеры. + +## Настройка региона на сервере + +Этот пример предполагает, что в WorldGuard существует регион с именем `spawn`. Создайте его командой: + +```text +/rg define spawn +``` + +Затем положите это меню в `plugins/AbstractMenus/menus/example/world-integrations/01-region-welcome/menu.conf` и выполните `/am reload`. Вход в регион запустит меню. + +## Похожие активаторы + +- **`regionLeave`** - такой же по форме, срабатывает, когда игрок выходит из региона. Удобно для сценариев "попрощаться" или "предупредить перед PvP". +- **`clickEntity` / `clickNPC` / `clickBlock`** - другие активаторы вида "мир триггерит меню". Полный список см. в разделе [Активаторы](/docs/ru/general/activators/). + +## Попробовать + +После установки пакета примеров: + +1. Установите [WorldGuard](https://dev.bukkit.org/projects/worldguard). +2. Создайте регион с именем `spawn` командой `/rg define spawn`. +3. Положите бандл в `plugins/AbstractMenus/menus/example/`. +4. `/am reload`. +5. Зайдите в регион. diff --git a/src/content/docs/ru/general/examples.mdx b/src/content/docs/ru/general/examples.mdx index 64d9a18..eba5e13 100644 --- a/src/content/docs/ru/general/examples.mdx +++ b/src/content/docs/ru/general/examples.mdx @@ -1,153 +1,23 @@ --- title: Примеры -description: Готовые меню для копирования с полным HOCON внутри. +description: Каталог готовых меню AbstractMenus - бери, копируй, подстраивай под себя. --- -import { Aside, Code } from "@astrojs/starlight/components"; +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; -import statsConf from "../../../../examples/simple/stats.conf?raw"; -import serversConf from "../../../../examples/simple/servers.conf?raw"; -import cakeShopConf from "../../../../examples/simple/cake_shop.conf?raw"; -import kitsConf from "../../../../examples/medium/kits.conf?raw"; -import craftConf from "../../../../examples/medium/craft.conf?raw"; -import shopConf from "../../../../examples/medium/shop.conf?raw"; -import enhancerConf from "../../../../examples/advanced/enhancer.conf?raw"; -import enchanterConf from "../../../../examples/advanced/enchanter.conf?raw"; +Каталог примеров переехал. Теперь есть отдельный раздел **Примеры** в сайдбаре - ~14 меню, разложенных по категориям использования. У каждого своя страница с подсвеченным кодом, бейджами фич и зависимостей, кнопками скачать одиночный файл или zip-бандл. -
Автор меню
+Начни со [страницы каталога](/docs/ru/examples/), либо сразу прыгай в нужную категорию: -Отсортировано по сложности. Полный HOCON каждого меню прямо здесь - скопируй код в файл в `plugins/AbstractMenus/menus/` и выполни `/am reload`. - - - -## Простые - -### Статистика игрока - -Читает ванильную статистику через PlaceholderAPI и рисует четыре головы. - -**Требуется:** PlaceholderAPI с установленными расширениями Player и Statistic. - -![preview-stats](/docs/img/examples/stats.gif) - -
-stats.conf - -
- -### Селектор серверов BungeeCord - -Лобби-стайл выбор сервера. Показывает онлайн-статус и количество игроков на каждом бэкенде. - -**Требуется:** BungeeCord, PlaceholderAPI с расширениями Player и Bungee. - -![preview-servers](/docs/img/examples/servers.gif) - -
-servers.conf - -
- -### Магазин тортов - -Покупка торта за внутриигровую валюту. - -**Требуется:** Vault и плагин экономики. - -![preview-cakes](/docs/img/examples/cakes.gif) - -
-cake_shop.conf - -
- -## Средние - -### Ежедневные киты - -Киты с проверкой прав и кулдауном через переменные. - -**Требуется:** LuckPerms. - -![preview-kits](/docs/img/examples/kits.gif) - -
-kits.conf - -
- -### Крафт - -Кастомная сетка крафта с drag-and-drop, работающая через правила `placedItem`. - -**Требуется:** Ничего. - -![preview-craft](/docs/img/examples/craft.gif) - -
-craft.conf - -
- -### Магазин - -Многокатегорийный пагинированный магазин с биндингами, вариантами лора и логикой скидок. - -**Требуется:** Vault и плагин экономики. - -![preview-shop](/docs/img/examples/shop.gif) - -
-shop.conf - -
- -## Продвинутые - -### Казино - -Меню в стиле игрового автомата с кадрами анимации и наградами. - -**Требуется:** Vault и плагин экономики. - -![preview-casino](/docs/img/examples/casino.gif) - -Казино состоит из нескольких файлов. Исходники: [advanced/casino на GitHub](https://github.com/AbstractMenus/examples/tree/main/advanced/casino). - -### Админ-панель - -Меню наказаний (kick/ban/mute) поверх каталога онлайн-игроков. - -**Требуется:** Essentials (или любой плагин, выдающий эти команды). - -![preview-admin](/docs/img/examples/padmin.gif) - -Несколько файлов. Исходники: [advanced/admin на GitHub](https://github.com/AbstractMenus/examples/tree/main/advanced/admin). - -### Усилитель предмета - -Прокачка предмета по кликам, состояние уровня хранится в переменных. - -**Требуется:** Ничего. - -![preview-enhancer](/docs/img/examples/enhancer.gif) - -
-enhancer.conf - -
- -### Случайное зачарование - -Выбивает случайное зачарование из таблицы с весами. - -**Требуется:** Ничего. - -![preview-enchant](/docs/img/examples/enchant.gif) - -
-enchanter.conf - -
+ + + + + + + + + + + + diff --git a/src/examples/advanced/enchanter.conf b/src/examples/advanced/enchanter.conf deleted file mode 100644 index b3892dc..0000000 --- a/src/examples/advanced/enchanter.conf +++ /dev/null @@ -1,115 +0,0 @@ -title: "Random enchanter" -size: 3 -activators { - command: "ench" -} -// Define draggable slots as 11 and 15 -draggable: [ - "---------", - "--x---x--", - "---------" -] -items: [ - { - slot: [ - "xxxxxxxxx", - "xx-xxx-xx", - "xxxx-xxxx" - ] - material: BLACK_STAINED_GLASS_PANE - name: " " - }, - // Set stub item by default - ${stub}, - { - slot: 22 - material: NETHER_STAR - name: "&bEnchant" - click: ${enchantAction} - } -] - -stub { - slot: 15 - material: WHITE_STAINED_GLASS_PANE - name: "&eResult" -} - -placeItemAction { - slot: 15 - serialized: %changed_item_serialized% - count: %changed_item_amount% -} - -enchantAction { - rules { - // If slot 11 is not empty - -placedItem { - slot: 11 - material: AIR - } - } - actions { - randActions: [ - { - placeItem: ${placeItemAction} { - enchantments { - DAMAGE_ALL: 1 - DURABILITY: 2 - } - } - }, - { - placeItem: ${placeItemAction} { - enchantments { - DIG_SPEED: 2 - } - } - }, - { - placeItem: ${placeItemAction} { - enchantments { - KNOCKBACK: 1 - } - } - }, - { - placeItem: ${placeItemAction} { - enchantments { - LUCK: 2 - DURABILITY: 1 - } - } - }, - { - placeItem: ${placeItemAction} { - enchantments { - FIRE_ASPECT: 1 - } - } - }, - { - placeItem: ${placeItemAction} { - enchantments { - PROTECTION_ENVIRONMENTAL: 2 - } - } - } - ] - removePlaced: 11 - sound: ENTITY_EXPERIENCE_ORB_PICKUP - } - denyActions { - sound: ENTITY_VILLAGER_NO - rules { - // If slot 15 is empty - placedItem { - slot: 15 - material: AIR - } - } - actions { - setButton: ${stub} - } - } -} \ No newline at end of file diff --git a/src/examples/advanced/enhancer.conf b/src/examples/advanced/enhancer.conf deleted file mode 100644 index e9c827d..0000000 --- a/src/examples/advanced/enhancer.conf +++ /dev/null @@ -1,118 +0,0 @@ -title: "Item enhancer" -size: 1 -activators { - command: "enhance" -} -// Define draggable slots as 2 and 6 -draggable: [ - "--x---x--" -] -items: [ - { - slot: [ - "xx-x-x-x-", - ] - material: BLACK_STAINED_GLASS_PANE - name: " " - }, - // Set stub item by default - ${stub}, - { - slot: 4 - material: NETHER_STAR - name: "&bEnhance" - click { - rules: ${enhanceRules} - actions { - removePlaced: 2 - sound: ENTITY_EXPERIENCE_ORB_PICKUP - } - denyActions { - sound: ENTITY_VILLAGER_NO - rules { - // If slot 6 is empty - placedItem { - slot: 6 - material: AIR - } - } - actions { - setButton: ${stub} - } - } - } - }, - { - slot: 8 - material: NAME_TAG - name: "&aInfo" - lore: [ - "", - "&fStone &7-> &bCoal ore", - "&fApple &7-> &bGolden apple", - "&fWooden sword &7-> &bDiamond sword", - "&fWooden axe &7-> &bDiamond axe", - ] - } -] - -stub { - slot: 6 - material: WHITE_STAINED_GLASS_PANE - name: "&eResult" -} - -placedRule { - slot: 2 -} - -placeAction { - slot: 6 - count: "%changed_item_amount%" -} - -enhanceRules { - // If at least one of the placedItem rule is true, stop it and return true - oneof: [ - { - placedItem: ${placedRule} { - material: STONE - } - actions { - placeItem: ${placeAction} { - material: COAL_ORE - } - } - }, - { - placedItem: ${placedRule} { - material: APPLE - } - actions { - placeItem: ${placeAction} { - material: GOLDEN_APPLE - } - } - }, - { - placedItem: ${placedRule} { - material: WOODEN_SWORD - } - actions { - placeItem: ${placeAction} { - material: DIAMOND_SWORD - } - } - }, - { - placedItem: ${placedRule} { - material: WOODEN_AXE - } - actions { - placeItem: ${placeAction} { - material: DIAMOND_AXE - } - } - } - ] -} \ No newline at end of file diff --git a/src/examples/medium/craft.conf b/src/examples/medium/craft.conf deleted file mode 100644 index 6cd538d..0000000 --- a/src/examples/medium/craft.conf +++ /dev/null @@ -1,185 +0,0 @@ -title: "Items crafting" -size: 1 -activators { - command: [ - "crafting", - "craft" - ] -} -items: [ - ${helmet.item} { // Include helmet template and extend it - slot: 1 - lore: [ - "&fRequired items:", - "&f - Leather (x5)" - ] - flags: HIDE_ATTRIBUTES - click { - left { - rules { - inventoryItems: ${helmet.requirements} - } - actions { - sound: ENTITY_VILLAGER_YES - itemAdd: ${helmet.item} - itemRemove: ${helmet.requirements} - } - denyActions: ${noEnoughItemsAction} - } - } - }, - ${chestplate.item} { - slot: 3 - lore: [ - "&fRequired items:", - "&f - Leather (x8)", - "&f - String (x1)", - "&f - Iron ingot (x1)" - ] - flags: HIDE_ATTRIBUTES - click { - left { - rules { - inventoryItems: ${chestplate.requirements} - } - actions { - sound: ENTITY_VILLAGER_YES - itemAdd: ${chestplate.item} - itemRemove: ${chestplate.requirements} - } - denyActions: ${noEnoughItemsAction} - } - } - }, - ${leggings.item} { - slot: 5 - lore: [ - "&fRequired items:", - "&f - Leather (x7)", - "&f - String (x1)" - ] - flags: HIDE_ATTRIBUTES - click { - left { - rules { - inventoryItems: ${leggings.requirements} - } - actions { - sound: ENTITY_VILLAGER_YES - itemAdd: ${leggings.item} - itemRemove: ${leggings.requirements} - } - denyActions: ${noEnoughItemsAction} - } - } - }, - ${boots.item} { - slot: 7 - lore: [ - "&fRequired items:", - "&f - Leather (x4)", - "&f - String (x2)" - ] - flags: HIDE_ATTRIBUTES - click { - left { - rules { - inventoryItems: ${boots.requirements} - } - actions { - sound: ENTITY_VILLAGER_YES - itemAdd: ${boots.item} - itemRemove: ${boots.requirements} - } - denyActions: ${noEnoughItemsAction} - } - } - }, -] - -noEnoughItemsAction { - sound: ENTITY_VILLAGER_NO - message: "&cYou doesn't have enough items for crafting it" -} - -helmet { - item { - material: LEATHER_HELMET - name: "&aHunter's hat" - enchantments { - DURABILITY: 2 - } - } - requirements: [ - { - material: LEATHER - count: 5 - } - ] -} - -chestplate { - item { - material: LEATHER_CHESTPLATE - name: "&aHunter's jacket" - enchantments { - DURABILITY: 2 - THORNS: 1 - } - } - requirements: [ - { - material: LEATHER - count: 8 - }, - { - material: STRING - count: 1 - }, - { - material: IRON_INGOT - count: 1 - } - ] -} - -leggings { - item { - material: LEATHER_LEGGINGS - name: "&aHunter's leggings" - enchantments { - DURABILITY: 2 - } - } - requirements: [ - { - material: LEATHER - count: 7 - }, - { - material: STRING - count: 1 - } - ] -} - -boots { - item { - material: LEATHER_BOOTS - name: "&aHunter's boots" - enchantments { - PROTECTION_PROJECTILE: 1 - DURABILITY: 2 - } - } - requirements: [ - { - material: LEATHER - count: 4 - }, - { - material: STRING - count: 2 - } - ] -} diff --git a/src/examples/medium/kits.conf b/src/examples/medium/kits.conf deleted file mode 100644 index c5ff196..0000000 --- a/src/examples/medium/kits.conf +++ /dev/null @@ -1,258 +0,0 @@ -title: "&0&lDaily kits" -size: 1 -activators { - command { - name: "kits" - aliases: "kit" - override: true - } -} -updateInterval: 20 -items: [ - ${default} { - lore: ${default.lore} [ // Extending lore by templates - "", - "&aClick, to get this kit" - ] - click { - itemAdd: [ // List of issued items - { material: LEATHER_HELMET }, - { material: LEATHER_CHESTPLATE }, - { material: LEATHER_LEGGINGS }, - { material: LEATHER_BOOTS }, - { material: WOODEN_SWORD }, - { - material: COOKED_BEEF - count: 32 - }, - { - material: IRON_INGOT - count: 32 - } - ] - setVarp: "kits_delay_default::true::1d" // Temporary variable that will be destroyed after 1 day - refreshMenu: true // Update menu to check rules again - } - }, - ${default} { // This item will display if variable is exists - lore: ${default.lore} [ - "", - "&cYou can get this kit after %varpt_:kits_delay_default%" - ] - rules { - existVarp: "kits_delay_default" - } - }, - ${vip} { - lore: ${vip.lore} [ - "", - "&aClick, to get this kit" - ] - click { - rules { - group: "vip" - } - actions { - itemAdd: [ - { material: LEATHER_HELMET }, - { material: LEATHER_CHESTPLATE }, - { material: LEATHER_LEGGINGS }, - { material: LEATHER_BOOTS }, - { material: STONE_SWORD }, - { - material: COOKED_BEEF - count: 32 - }, - { - material: IRON_INGOT - count: 32 - }, - { - material: GOLDEN_APPLE - count: 4 - } - ] - setVarp: "kits_delay_vip::true::1d" - refreshMenu: true - } - denyActions { - sound: ENTITY_VILLAGER_NO - message: "&cYou are not a memeber of VIP group" - } - } - }, - ${vip} { - lore: ${vip.lore} [ - "", - "&cYou can get this kit after %varpt_:kits_delay_vip%" - ] - rules { - existVarp: "kits_delay_vip" - } - }, - ${premium} { - lore: ${premium.lore} [ - "", - "&aClick, to get this kit" - ] - click { - rules { - group: "premium" - } - actions { - itemAdd: [ - { material: IRON_HELMET }, - { material: IRON_CHESTPLATE }, - { material: IRON_LEGGINGS }, - { material: IRON_BOOTS }, - { material: IRON_SWORD }, - { - material: COOKED_BEEF - count: 64 - }, - { - material: IRON_INGOT - count: 16 - }, - { - material: DIAMOND - count: 16 - }, - { - material: GOLDEN_APPLE - count: 6 - } - ] - setVarp: "kits_delay_premium::true::1d" - refreshMenu: true - } - denyActions { - sound: ENTITY_VILLAGER_NO - message: "&cYou are not a memeber of Premium group" - } - } - }, - ${premium} { - lore: ${premium.lore} [ - "", - "&cYou can get this kit after %varpt_:kits_delay_premium%" - ] - rules{ - existVarp: "kits_delay_premium" - } - }, - ${ultra} { - lore: ${ultra.lore} [ - "", - "&aClick, to get this kit" - ] - click { - rules { - group: "ultra" - } - actions { - itemAdd: [ - { material: DIAMOND_HELMET }, - { material: DIAMOND_CHESTPLATE }, - { material: DIAMOND_LEGGINGS }, - { material: DIAMOND_BOOTS }, - { material: DIAMOND_SWORD }, - { - material: COOKED_BEEF - count: 64 - }, - { - material: IRON_INGOT - count: 32 - }, - { - material: DIAMOND - count: 32 - }, - { - material: GOLDEN_APPLE - count: 16 - } - ] - setVarp: "kits_delay_ultra::true::1d" - refreshMenu: true - } - denyActions { - sound: ENTITY_VILLAGER_NO - message: "&cYou are not a memeber of Ultra group" - } - } - }, - ${ultra} { - lore: ${ultra.lore} [ - "", - "&cYou can get this kit after %varpt_:kits_delay_ultra%" - ] - rules { - existVarp: "kits_delay_ultra" - } - }, -] - -# Templates of items - -default { // Default kit template - slot: 1 - material: WOODEN_SWORD - name: "&aDefault kit" - flags: HIDE_ATTRIBUTES // A special flag to hide sword damage info - lore: [ - "&7Includes:", - "&7 - Leather armor set", - "&7 - Wooden sword", - "&7 - 32 beef", - "&7 - 16 iron ingots" - ] -} - -vip { // VIP kit template - slot: 3 - material: STONE_SWORD - name: "&6VIP kit" - flags: HIDE_ATTRIBUTES - lore: [ - "&7Includes:", - "&7 - Leather armor set", - "&7 - Stone sword", - "&7 - 32 beef", - "&7 - 32 iron ingots", - "&7 - 4 gold apple" - ] -} - -premium { // Premium kit template - slot: 5 - material: IRON_SWORD - name: "&bPremium kit" - flags: HIDE_ATTRIBUTES - lore: [ - "&7Includes:", - "&7 - Iron armor kit", - "&7 - Iron sword", - "&7 - 64 beef", - "&7 - 16 iron ingots", - "&7 - 16 diamonds", - "&7 - 6 gold apple" - ] -} - -ultra { // Ultra kit template - slot: 7 - material: DIAMOND_SWORD - name: "&cUltra kit" - flags: HIDE_ATTRIBUTES - lore: [ - "&7Includes:", - "&7 - Diamond armor kit", - "&7 - Diamond sword", - "&7 - 64 beef", - "&7 - 32 iron ingots", - "&7 - 32 diamonds", - "&7 - 16 gold apple" - ] -} diff --git a/src/examples/medium/shop.conf b/src/examples/medium/shop.conf deleted file mode 100644 index 2384bf7..0000000 --- a/src/examples/medium/shop.conf +++ /dev/null @@ -1,702 +0,0 @@ -menus { // Add several menus to one file - shop { // Menu with name "shop" - title: "Items shop" - size: 1 - activators { - command: "shop" - } - items: [ - { - slot: 2 - material: GRASS_BLOCK - name: "&aBlocks" - click { - openMenu: "shop_blocks" - } - }, - { - slot: 3 - material: BREAD - name: "&aFood" - click { - openMenu: "shop_food" - } - }, - { - slot: 4 - material: IRON_CHESTPLATE - name: "&aArmor" - flags: HIDE_ATTRIBUTES - click { - openMenu: "shop_armor" - } - }, - { - slot: 5 - material: IRON_SWORD - name: "&aWeapon" - flags: HIDE_ATTRIBUTES - click { - openMenu: "shop_weapon" - } - }, - { - slot: 6 - material: DIAMOND_PICKAXE - name: "&aTools" - flags: HIDE_ATTRIBUTES - click { - openMenu: "shop_tools" - } - } - ] - } - - shop_blocks { // Sub menu with name "shop_blocks" - title: "Blocks" - size: 1 - items: [ - { - slot: 0 - material: STONE - count: 32 - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { - material: STONE - count: 32 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 1 - material: GRASS_BLOCK - count: 32 - lore: [ "", "&7Price: &e70", "", "&aClick, to buy" ] - click { - rules { money: 70 } - actions { - itemAdd { - material: GRASS_BLOCK - count: 32 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 2 - material: OAK_WOOD - count: 16 - lore: [ "", "&7Price: &e90", "", "&aClick, to buy" ] - click { - rules { money: 90 } - actions { - itemAdd { - material: OAK_WOOD - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 3 - material: SAND - count: 32 - lore: [ "", "&7Price: &e120", "", "&aClick, to buy" ] - click { - rules { money: 120 } - actions { - itemAdd { - material: SAND - count: 32 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 4 - material: GLASS - count: 32 - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { - material: GLASS - count: 32 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 5 - material: WHITE_WOOL - count: 16 - lore: [ "", "&7Price: &e200", "", "&aClick, to buy" ] - click { - rules { money: 200 } - actions { - itemAdd { - material: WHITE_WOOL - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 6 - material: BRICKS - count: 32 - lore: [ "", "&7Price: &e300", "", "&aClick, to buy" ] - click { - rules { money: 300 } - actions { - itemAdd { - material: BRICKS - count: 32 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 8 - texture: "bd69e06e5dadfd84e5f3d1c21063f2553b2fa945ee1d4d7152fdc5425bc12a9" // Хеш скина из ссылки http://textures.minecraft.net/texture/ - name: "&cBack" - click { - openMenu: "shop" - } - } - ] - } - - shop_food { // Sub menu with name "shop_food" - title: "Food" - size: 1 - items: [ - { - slot: 0 - material: APPLE - count: 16 - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { - material: APPLE - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 1 - material: BREAD - count: 8 - lore: [ "", "&7Price: &e120", "", "&aClick, to buy" ] - click { - rules { money: 120 } - actions { - itemAdd { - material: BREAD - count: 8 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 2 - material: CAKE - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { material: CAKE } - } - denyActions: ${denyAction} - } - }, - { - slot: 3 - material: APPLE - count: 16 - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { - material: STONE - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 4 - material: COOKED_BEEF - count: 16 - lore: [ "", "&7Price: &e130", "", "&aClick, to buy" ] - click { - rules { money: 130 } - actions { - itemAdd { - material: COOKED_BEEF - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 5 - material: MUSHROOM_STEW - count: 8 - lore: [ "", "&7Price: &e200", "", "&aClick, to buy" ] - click { - rules { money: 200 } - actions { - itemAdd { - material: MUSHROOM_STEW - count: 8 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 8 - texture: "bd69e06e5dadfd84e5f3d1c21063f2553b2fa945ee1d4d7152fdc5425bc12a9" - name: "&cBack" - click { - openMenu: "shop" - } - } - ] - } - - shop_armor{ // Sub menu with name "shop_armor" - title: "Armor" - size: 6 - items: [ - { - slot: "3,2" - material: LEATHER_HELMET - lore: [ "", "&7Price: &e80", "", "&aClick, to buy" ] - click { - rules { money: 80 } - actions { - itemAdd { material: LEATHER_HELMET } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,3" - material: LEATHER_CHESTPLATE - lore: [ "", "&7Price: &e120", "", "&aClick, to buy" ] - click { - rules { money: 120 } - actions { - itemAdd { material: LEATHER_CHESTPLATE } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,4" - material: LEATHER_LEGGINGS - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { material: LEATHER_LEGGINGS } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,5" - material: LEATHER_BOOTS - lore: [ "", "&7Price: &e70", "", "&aClick, to buy" ] - click { - rules { money: 70 } - actions { - itemAdd { material: LEATHER_BOOTS } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,2" - material: IRON_HELMET - lore: [ "", "&7Price: &e200", "", "&aClick, to buy" ] - click { - rules { money: 200 } - actions { - itemAdd { material: IRON_HELMET } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,3" - material: IRON_CHESTPLATE - lore: [ "", "&7Price: &e300", "", "&aClick, to buy" ] - click { - rules { money: 300 } - actions { - itemAdd { material: IRON_CHESTPLATE } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,4" - material: IRON_LEGGINGS - lore: [ "", "&7Price: &e250", "", "&aClick, to buy" ] - click { - rules { money: 250 } - actions { - itemAdd { material: IRON_LEGGINGS } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,5" - material: IRON_BOOTS - lore: [ "", "&7Price: &e180", "", "&aClick, to buy" ] - click { - rules { money: 180 } - actions { - itemAdd { material: IRON_BOOTS } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,2" - material: DIAMOND_HELMET - lore: [ "", "&7Price: &e500", "", "&aClick, to buy" ] - click { - rules { money: 500 } - actions { - itemAdd { material: DIAMOND_HELMET } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,3" - material: DIAMOND_CHESTPLATE - lore: [ "", "&7Price: &e800", "", "&aClick, to buy" ] - click { - rules { money: 800 } - actions { - itemAdd { material: DIAMOND_CHESTPLATE } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,4" - material: DIAMOND_LEGGINGS - lore: [ "", "&7Price: &e700", "", "&aClick, to buy" ] - click { - rules { money: 700 } - actions { - itemAdd { material: DIAMOND_LEGGINGS } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,5" - material: DIAMOND_BOOTS - lore: [ "", "&7Price: &e450", "", "&aClick, to buy" ] - click { - rules { money: 450 } - actions { - itemAdd { material: DIAMOND_BOOTS } - } - denyActions: ${denyAction} - } - }, - { - slot: "1,6" - texture: "bd69e06e5dadfd84e5f3d1c21063f2553b2fa945ee1d4d7152fdc5425bc12a9" - name: "&cBack" - click { - openMenu: "shop" - } - } - ] - } - - shop_weapon{ // Sub menu with name "shop_weapon" - title: "Weapon" - size: 1 - items: [ - { - slot: 0 - material: WOODEN_SWORD - lore: [ "", "&7Price: &e50", "", "&aClick, to buy" ] - click { - rules { money: 50 } - actions { - itemAdd { material: WOODEN_SWORD } - } - denyActions: ${denyAction} - } - }, - { - slot: 1 - material: STONE_SWORD - lore: [ "", "&7Price: &e80", "", "&aClick, to buy" ] - click { - rules { money: 80 } - actions { - itemAdd { material: STONE_SWORD } - } - denyActions: ${denyAction} - } - }, - { - slot: 2 - material: IRON_SWORD - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { material: IRON_SWORD } - } - denyActions: ${denyAction} - } - }, - { - slot: 3 - material: DIAMOND_SWORD - lore: [ "", "&7Price: &e250", "", "&aClick, to buy" ] - click { - rules { money: 250 } - actions { - itemAdd { material: DIAMOND_SWORD } - } - denyActions: ${denyAction} - } - }, - { - slot: 4 - material: BOW - lore: [ "", "&7Price: &e200", "", "&aClick, to buy" ] - click { - rules { money: 200 } - actions { - itemAdd { material: BOW } - } - denyActions: ${denyAction} - } - }, - { - slot: 5 - material: ARROW - count: 16 - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { - material: ARROW - count: 16 - } - } - denyActions: ${denyAction} - } - }, - { - slot: 8 - texture: "bd69e06e5dadfd84e5f3d1c21063f2553b2fa945ee1d4d7152fdc5425bc12a9" - name: "&cBack" - click { - openMenu: "shop" - } - } - ] - } - - shop_tools { // Sub menu with name "shop_tools" - title: "Tools" - size: 6 - items: [ - { - slot: "3,2" - material: STONE_SHOVEL - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { material: STONE_SHOVEL } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,3" - material: STONE_PICKAXE - lore: [ "", "&7Price: &e120", "", "&aClick, to buy" ] - click { - rules { money: 120 } - actions { - itemAdd { material: STONE_PICKAXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,4" - material: STONE_AXE - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { material: STONE_AXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "3,5" - material: STONE_HOE - lore: [ "", "&7Price: &e100", "", "&aClick, to buy" ] - click { - rules { money: 100 } - actions { - itemAdd { material: STONE_HOE } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,2" - material: IRON_SHOVEL - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { material: IRON_SHOVEL } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,3" - material: IRON_PICKAXE - lore: [ "", "&7Price: &e160", "", "&aClick, to buy" ] - click { - rules { money: 160 } - actions { - itemAdd { material: IRON_PICKAXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,4" - material: IRON_AXE - lore: [ "", "&7Price: &e200", "", "&aClick, to buy" ] - click { - rules { money: 200 } - actions { - itemAdd { material: IRON_AXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "5,5" - material: IRON_HOE - lore: [ "", "&7Price: &e150", "", "&aClick, to buy" ] - click { - rules { money: 150 } - actions { - itemAdd { material: IRON_HOE } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,2" - material: DIAMOND_SHOVEL - lore: [ "", "&7Price: &e300", "", "&aClick, to buy" ] - click { - rules { money: 300 } - actions { - itemAdd { material: DIAMOND_SHOVEL } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,3" - material: DIAMOND_PICKAXE - lore: [ "", "&7Price: &e320", "", "&aClick, to buy" ] - click { - rules { money: 320 } - actions { - itemAdd { material: DIAMOND_PICKAXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,4" - material: DIAMOND_AXE - lore: [ "", "&7Price: &e350", "", "&aClick, to buy" ] - click { - rules { money: 350 } - actions { - itemAdd { material: DIAMOND_AXE } - } - denyActions: ${denyAction} - } - }, - { - slot: "7,5" - material: DIAMOND_HOE - lore: [ "", "&7Price: &e300", "", "&aClick, to buy" ] - click { - rules { money: 300 } - actions { - itemAdd { material: DIAMOND_HOE } - } - denyActions: ${denyAction} - } - }, - { - slot: "1,6" - texture: "bd69e06e5dadfd84e5f3d1c21063f2553b2fa945ee1d4d7152fdc5425bc12a9" - name: "&cBack" - click { - openMenu: "shop" - } - } - ] - } -} - -denyAction { - sound: ENTITY_VILLAGER_NO - message: "&cYou don't have enough money" -} diff --git a/src/examples/simple/cake_shop.conf b/src/examples/simple/cake_shop.conf deleted file mode 100644 index 24bf79b..0000000 --- a/src/examples/simple/cake_shop.conf +++ /dev/null @@ -1,74 +0,0 @@ -title: "&0Cake shop" -size: 3 -activators { - command: "cakes" -} -items: [ - { - slot: [ - "xxxxxxxxx" - "xxx-x-xxx" - "xxxxxxxx-" - ] - material: BLACK_STAINED_GLASS_PANE - name: " " - }, - { - slot: 12 - material: CAKE - name: "&aBuy cake" - lore: [ - "", - "&7Price: &e100$" - ] - click { - rules { - money: 100 - } - actions { - takeMoney: 100 - itemAdd { - material: CAKE - } - } - denyActions { - sound: ENTITY_VILLAGER_NO - message: "You have no enough money to buy this cake" - } - } - }, - { - slot: 14 - material: COOKIE - count: 16 - name: "&aBuy cookie" - lore: [ - "", - "&7Price: &e75$" - ] - click { - rules { - money: 75 - } - actions { - takeMoney: 75 - itemAdd { - material: COOKIE - count: 16 - } - } - denyActions { - sound: ENTITY_VILLAGER_NO - message: "You have no enough money to buy cookies" - } - } - }, - { - slot: 26 - material: BARRIER - name: "&cClose" - click { - closeMenu: true - } - } -] \ No newline at end of file diff --git a/src/examples/simple/servers.conf b/src/examples/simple/servers.conf deleted file mode 100644 index e3c61b1..0000000 --- a/src/examples/simple/servers.conf +++ /dev/null @@ -1,121 +0,0 @@ -title: "Game menu" -size: 5 -activators { - command: "games" // Command to open menu -} -items: [ - { - slot: "1, 2" - material: BOOKSHELF - name: "&e&lHub" - lore: [ - "&8Main lobby", - "", - "&a> Click to connect", - "&7Online: &e%bungee_lobby%" - ] - glow: true - click { - bungeeConnect: "lobby" // Connect to "lobby" server after click - } - }, - { - slot: "3, 3" - material: BRICKS - name: "&e&lBuild Battle" - lore: [ - "&8Casual Games", - "", - "&a> Click to connect", - "&7Online: &e%bungee_bb%" - ] - click { - bungeeConnect: "bb" - } - }, - { - slot: "4, 2" - material: ENDER_EYE - name: "&e&lSky Wars" - lore: [ - "&8Survival", - "", - "&a> Click to connect", - "&7Online: &e%bungee_sw%" - ] - click { - bungeeConnect: "sw" - } - }, - { - slot: "5, 3" - material: BOW - name: "&e&lMurder Mystery" - lore: [ - "&8Survival", - "", - "&a> Click to connect", - "&7Online: &e%bungee_mm%" - ] - click { - bungeeConnect: "mm" - } - }, - { - slot: "6, 2" - material: TNT - name: "&e&lTNT Games" - lore: [ - "&8Casual games", - "", - "&a> Click to connect", - "&7Online: &e%bungee_tnt%" - ] - click { - bungeeConnect: "tnt" - } - }, - { - slot: "7, 3" - material: GRASS_BLOCK - name: "&e&lSky Block" - lore: [ - "&8Survival", - "", - "&a> Click to connect", - "&7Online: &e%bungee_sb%" - ] - click { - bungeeConnect: "sb" - } - }, - { - slot: "4, 4" - material: FILLED_MAP - name: "&e&lSurvival" - lore: [ - "&8Survival", - "", - "&a> Click to connect", - "&7Online: &e%bungee_survival%" - ] - click { - bungeeConnect: "survival" - } - }, - { - slot: "6, 4" - material: IRON_SWORD - name: "&e&lBattle Royal" - lore: [ - "&8Survival", - "", - "&a> Click to connect", - "&7Online: &e%bungee_br%" - ] - flags: HIDE_ATTRIBUTES - click { - bungeeConnect: "br" - } - } -] diff --git a/src/examples/simple/stats.conf b/src/examples/simple/stats.conf deleted file mode 100644 index bccdaee..0000000 --- a/src/examples/simple/stats.conf +++ /dev/null @@ -1,36 +0,0 @@ -title: "&0&l%player_name%'s stats" -size: 4 -activators { - command: "stats" -} -items: [ - { - slot: "2, 2" - texture: "ebfe9b7af68dc1264a6fc969f3d7b08e50e61e6ee91cea83daabd18820f0ef" - name: "&6Player kills &7(&c%statistic_player_kills%&7)" - }, - { - slot: "4, 2" - texture: "1ae3855f952cd4a03c148a946e3f812a5955ad35cbcb52627ea4acd47d3081" - name: "&6Deaths &7(&c%statistic_deaths%&7)" - }, - { - slot: "6, 2" - texture: "f67b27fb7e29ec98e1cd4a8f8466856d9ef3f2e9fbd9aed6311f8abe54b6ab2" - name: "&6Mob kills &7(&c%statistic_mob_kills%&7)" - }, - { - slot: "8, 2" - texture: "f32c6171532a2a87f0eeb28edd010833f33f0ae6841a524e1b5200a35d385050" - name: "&6Hours played &7(&c%statistic_hours_played% ч&7)" - }, - { - slot: "5, 4" - texture: "5a6787ba32564e7c2f3a0ce64498ecbb23b89845e5a66b5cec7736f729ed37" - name: "&cClose" - lore: "&7Click, to close menu" - click { - closeMenu: true - } - } -] diff --git a/src/styles/examples.css b/src/styles/examples.css new file mode 100644 index 0000000..e82eb4e --- /dev/null +++ b/src/styles/examples.css @@ -0,0 +1,544 @@ +/* src/styles/examples.css */ + +.menu-meta { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin: 1rem 0; + padding: 0.75rem; + border-radius: 0.5rem; + background: var(--sl-color-gray-6, #f5f5f5); +} + +.meta-row { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + align-items: center; +} + +.meta-label { + font-size: 0.85rem; + font-weight: 600; + color: var(--sl-color-gray-2); +} + +.badge { + display: inline-block; + padding: 0.2rem 0.55rem; + border-radius: 0.4rem; + font-size: 0.8rem; + font-weight: 500; + background: var(--sl-color-gray-5, #e5e5e5); + color: var(--sl-color-text); +} + +.badge-level.badge-beginner { background: #16a34a; color: white; } +.badge-level.badge-intermediate { background: #f59e0b; color: white; } +.badge-level.badge-advanced { background: #dc2626; color: white; } + +.badge-version { background: var(--sl-color-gray-4); } + +.badge-feature { background: #1e40af; color: white; } +.badge-dep { background: #7c3aed; color: white; } + +/* "Open: /command [copy]" row */ + +.meta-row-cmd { + align-items: center; +} + +.meta-cmd { + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 12%)); + border: 1px solid var(--sl-color-gray-5); + padding: 0.15rem 0.55rem; + border-radius: 0.3rem; + font-size: 0.9rem; + font-family: var(--sl-font-mono, monospace); +} + +/* Ant Design-style ghost copy button: no border/bg, icon-only, + tooltip on hover, success color on copy. */ +.meta-cmd-copy { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + padding: 0; + background: transparent; + border: none; + cursor: pointer; + color: var(--sl-color-gray-2); + line-height: 1; + flex-shrink: 0; + transition: color 0.15s ease; +} + +.meta-cmd-copy:hover { + color: var(--sl-color-text-accent, var(--sl-color-text)); +} + +.meta-cmd-copy.copied { + color: #15803d; +} + +/* Both SVGs stack on top of each other, opacity-swap avoids layout shift. */ +.meta-cmd-icon { + position: absolute; + inset: 0; + margin: auto; + width: 16px; + height: 16px; + transition: opacity 0.15s ease; +} + +.meta-cmd-icon-check { opacity: 0; } +.meta-cmd-copy.copied .meta-cmd-icon-copy { opacity: 0; } +.meta-cmd-copy.copied .meta-cmd-icon-check { opacity: 1; } + +/* Tooltip - hidden by default, shown on hover or while copied state is active */ +.meta-cmd-tooltip { + position: absolute; + bottom: calc(100% + 0.4rem); + left: 50%; + transform: translateX(-50%); + padding: 0.25rem 0.55rem; + background: var(--sl-color-bg-nav, #1f2937); + color: var(--sl-color-white, #fff); + border-radius: 0.3rem; + font-size: 0.75rem; + font-weight: 500; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease, transform 0.15s ease; + z-index: 100; +} + +/* Tooltip arrow (small triangle pointing down to the button) */ +.meta-cmd-tooltip::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 4px solid transparent; + border-top-color: var(--sl-color-bg-nav, #1f2937); +} + +.meta-cmd-copy:hover .meta-cmd-tooltip, +.meta-cmd-copy:focus-visible .meta-cmd-tooltip, +.meta-cmd-copy.copied .meta-cmd-tooltip { + opacity: 1; +} + +.meta-cmd-none { + font-style: italic; + color: var(--sl-color-gray-2); + font-size: 0.9rem; +} + +.menu-example { + margin: 1.5rem 0; +} + +.download-card { + margin-top: 1rem; + padding: 1rem; + border: 1px solid var(--sl-color-gray-5); + border-radius: 0.5rem; + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 8%)); +} + +.dl-row { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.dl-btn { + display: inline-block; + padding: 0.4rem 0.9rem; + border-radius: 0.4rem; + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + border: none; + text-decoration: none; +} + +.dl-btn-primary { + background: var(--sl-color-accent, #f05454); + color: white; +} + +.dl-btn-primary:hover { opacity: 0.9; } + +.dl-btn-secondary { + background: var(--sl-color-gray-5); + color: var(--sl-color-text); +} + +.dl-btn-secondary:hover { background: var(--sl-color-gray-4); } + +.dl-hint { + margin: 0.5rem 0 0; + font-size: 0.85rem; + color: var(--sl-color-gray-2); +} + +/* Pack Builder */ + +.pack-builder { + margin: 1.5rem 0; +} + +.pb-header { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; + padding: 0.75rem; + border: 1px solid var(--sl-color-gray-5); + border-radius: 0.5rem; + background: var(--sl-color-bg, white); + margin-bottom: 1rem; + position: sticky; + /* Sit below Starlight's top nav bar. Generous fallback for narrow widths + where --sl-nav-height isn't always set or reports wrong height. */ + top: calc(var(--sl-nav-height, 5rem) + 1rem); + z-index: 10; +} + +/* Starlight's nav height varies by breakpoint and so does our needed offset. + The default `top` works for desktop (>= ~880px) and mobile (< ~600px). + Mid-range widths use a single-line nav that's taller relative to its + declared --sl-nav-height value, so bump the offset there. */ +@media (min-width: 600px) and (max-width: 1100px) { + .pb-header { + top: calc(var(--sl-nav-height, 5rem) + 2rem); + } +} + +/* All header items: identical height, internal flex centering */ +.pb-header > .dl-btn, +.pb-header > .pb-summary { + height: 2.25rem; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + box-sizing: border-box; + margin: 0; + line-height: 1; +} + +.pb-summary { + flex: 1 1 8rem; + min-width: 0; + font-size: 0.9rem; + color: var(--sl-color-gray-2); + overflow: hidden; + text-overflow: ellipsis; +} + +.pb-header [data-action="build-zip"] { + margin-left: auto; +} + +/* Ghost icon button used for "expand/collapse all" - same shape as + MenuMeta's copy button (no border/bg, tooltip on hover). */ +.pb-icon-btn { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.85rem; + height: 1.85rem; + padding: 0; + background: transparent; + border: none; + cursor: pointer; + color: var(--sl-color-gray-2); + transition: color 0.15s ease; + flex-shrink: 0; + box-sizing: border-box; + margin: 0; +} + +.pb-icon-btn:hover { + color: var(--sl-color-text-accent, var(--sl-color-text)); +} + +/* The two chevron icons are stacked; class flips which one is visible. */ +.pb-toggle-icon { + position: absolute; + inset: 0; + margin: auto; + width: 16px; + height: 16px; + transition: opacity 0.15s ease; +} + +.pb-toggle-icon-collapse { opacity: 0; } +.pb-icon-btn.is-collapse .pb-toggle-icon-expand { opacity: 0; } +.pb-icon-btn.is-collapse .pb-toggle-icon-collapse { opacity: 1; } + +.pb-icon-tooltip { + position: absolute; + bottom: calc(100% + 0.4rem); + left: 50%; + transform: translateX(-50%); + padding: 0.25rem 0.55rem; + background: var(--sl-color-bg-nav, #1f2937); + color: var(--sl-color-white, #fff); + border-radius: 0.3rem; + font-size: 0.75rem; + font-weight: 500; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; + z-index: 100; +} + +.pb-icon-tooltip::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 4px solid transparent; + border-top-color: var(--sl-color-bg-nav, #1f2937); +} + +.pb-icon-btn:hover .pb-icon-tooltip, +.pb-icon-btn:focus-visible .pb-icon-tooltip { + opacity: 1; +} + +/* Floating action button at the bottom-right of the sidebar - toggles all + groups expand/collapse. Inserted at runtime by the head script as the + last child of .sidebar-pane. */ +.sl-fab { + position: sticky; + bottom: 1rem; + margin-left: auto; + margin-right: 0.5rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + width: 2.25rem; + height: 2.25rem; + padding: 0; + background: var(--sl-color-bg-nav, var(--sl-color-bg, white)); + color: var(--sl-color-gray-2); + border: 1px solid var(--sl-color-hairline-shade, var(--sl-color-gray-5)); + border-radius: 50%; + cursor: pointer; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06); + transition: color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease; + z-index: 5; + flex-shrink: 0; +} + +.sl-fab:hover { + color: var(--sl-color-text-accent, var(--sl-color-text)); + background: var(--sl-color-bg, white); + transform: translateY(-1px); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.08); +} + +.sl-fab:active { + transform: translateY(0); +} + +.sl-fab-icon { + position: absolute; + inset: 0; + margin: auto; + width: 18px; + height: 18px; + pointer-events: none; + transition: opacity 0.15s ease; +} + +.sl-fab-icon-collapse { opacity: 0; } +.sl-fab.is-collapse .sl-fab-icon-expand { opacity: 0; } +.sl-fab.is-collapse .sl-fab-icon-collapse { opacity: 1; } + +.sl-fab-tooltip { + position: absolute; + right: calc(100% + 0.5rem); + top: 50%; + transform: translateY(-50%); + padding: 0.3rem 0.6rem; + background: var(--sl-color-bg-nav, #1f2937); + color: var(--sl-color-white, #fff); + border-radius: 0.3rem; + font-size: 0.75rem; + font-weight: 500; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; +} + +.sl-fab-tooltip::after { + content: ''; + position: absolute; + left: 100%; + top: 50%; + transform: translateY(-50%); + border: 4px solid transparent; + border-left-color: var(--sl-color-bg-nav, #1f2937); +} + +.sl-fab:hover .sl-fab-tooltip, +.sl-fab:focus-visible .sl-fab-tooltip { + opacity: 1; +} + +.pb-cat { + margin: 0.5rem 0; + border: 1px solid var(--sl-color-gray-5); + border-radius: 0.5rem; + padding: 0.5rem 0.75rem; +} + +.pb-cat[open] { + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 4%)); +} + +.pb-cat summary { + cursor: pointer; + font-weight: 600; + font-size: 1rem; + padding: 0.25rem 0; + display: flex; + gap: 0.5rem; + align-items: center; + list-style: none; /* hide default ▶ disclosure triangle, we render our own */ +} + +.pb-cat summary::-webkit-details-marker { + display: none; +} + +/* IDEA-style chevron that points right when collapsed, rotates 90deg when open. */ +.pb-cat-chevron { + flex-shrink: 0; + color: var(--sl-color-gray-2); + transition: transform 0.15s ease; +} + +.pb-cat[open] .pb-cat-chevron { + transform: rotate(90deg); +} + +.pb-cat-toggle { + margin-right: 0.25rem; + cursor: pointer; +} + +.pb-cat-name { + flex: 1; + font-family: monospace; +} + +.pb-cat-count { + font-size: 0.8rem; + color: var(--sl-color-gray-2); + font-weight: 400; +} + +.pb-list { + list-style: none; + margin: 0; + padding: 0.5rem 0 0 1.5rem; +} + +.pb-list li { + margin: 0.25rem 0; +} + +.pb-list label { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + align-items: center; + cursor: pointer; + padding: 0.2rem 0.4rem; + border-radius: 0.3rem; +} + +.pb-list label:hover { + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 8%)); +} + +.pb-menu { + margin-right: 0.4rem; + cursor: pointer; +} + +.pb-title { + flex: 1; + font-size: 0.9rem; + min-width: 10rem; +} + +/* Category index table */ + +.cat-index { + width: 100%; + margin: 1rem 0; + font-size: 0.9rem; + border-collapse: collapse; +} + +.cat-index th, +.cat-index td { + padding: 0.5rem 0.6rem; + text-align: left; + vertical-align: top; + border-bottom: 1px solid var(--sl-color-gray-5); +} + +.cat-index th { + font-weight: 600; + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 8%)); +} + +/* Don't use flex on the td - it breaks vertical-align: top on the cell. + Inline-block flow with margins respects the row's top-align rule. */ +.cat-features { + max-width: 18rem; +} + +.cat-feature { + display: inline-block; + font-size: 0.75rem; + padding: 0.1rem 0.35rem; + margin: 0 0.25rem 0.25rem 0; + border-radius: 0.25rem; + background: var(--sl-color-bg-inline-code, rgb(128 128 128 / 10%)); + border: 1px solid var(--sl-color-gray-5); +} + +.cat-more { + font-size: 0.75rem; + color: var(--sl-color-gray-2); +} + +.cat-none { + color: var(--sl-color-gray-3); +} + +.cat-cmd { + font-size: 0.8rem; + white-space: nowrap; +}