AxinMenuGUI

Crea menús GUI completamente configurables para tu servidor VintageStory — sin escribir código.

AxinMenuGUI es un mod de menús interactivos para VintageStory que permite a los administradores construir menús complejos y multipágina mediante ficheros JSON. Sin programar, sin reiniciar el servidor.

Inspirado en GUIPlus (Minecraft/Spigot), diseñado desde cero para la API de VintageStory y la metodología AXIN.

🖼
100% JSON
Crea y edita menús con ficheros JSON. Recarga en caliente sin reiniciar con /amenu reload.
🎨
14 Temas visuales
Paneles con colores personalizados: oscuros, pergamino, piedra, noche, cofre, cristal. Un tema por escena.
🖱
Click Events
Mensajes, comandos, dar/quitar ítems, navegar entre escenas, abrir submenús y más.
Condiciones
Muestra u oculta botones según permisos, ítems en inventario, cooldowns y variables.
📄
Multi-escena
Menús multipágina con navegación y tema visual diferente en cada escena.
💾
Player Stats
Kills, muertes, tiempo de juego, puntos por mob. Ranking de jugadores con caché.
🔗
Tokens dinámicos
{player}, {stats.deaths}, {ranking.position} y más en cualquier texto del menú.
🖼
58 Iconos propios
Iconos pixel art 32×32 en el dominio axinmenugui: listos para usar en cualquier botón.
🌀
Sistema de Teleport
Teleport a coordenadas, puntos ATP guardados por nombre y teletransporte aleatorio seguro (/artp) con validación de terreno.
🧱
Block Click Triggers
Clic derecho en un bloque vinculado abre el menú configurado. Persistencia en menusClick.json.
🏪
Mercado Global P2P
Mercado entre jugadores con escrow automático. Compra, venta rápida y asistente de venta personalizada. Integrado con Bank y Treasury.
🏦
Bank (bloque)
Cofre especial crafteable. Sus ítems se usan automáticamente como fuente de pago en el mercado (solo cuando está cerrado).
🎯
KillRanking Missions
Hitos de puntos de ranking: recompensa automática (ítem, mensaje, comando) la primera vez que el jugador cruza cada umbral.

📋 Requisitos

RequisitoValor
VintageStory1.21.x+
Versión actual0.9.5
Tipo de modUniversal (cliente y servidor)
DependenciasNinguna (mod standalone)
Distribución al cliente — obligatoria

AxinMenuGUI renderiza GUI en el cliente. Debe estar instalado en el cliente además del servidor.

🚀 Inicio rápido

Instala AxinMenuGUI y crea tu primer menú en minutos.

📥 Instalación

  1. Copia AxinMenuGUI_v0.9.5.zip en la carpeta Mods/ del servidor.
  2. Reinicia el servidor. Se generan los ficheros de configuración y menús de ejemplo.
  3. Verifica en los logs: [AxinMenuGUI] Alias registrado: /menu → 'ejemplo'
  4. Entra al juego y escribe /menu para ver el menú de ejemplo.
💡
El mod es Universal: también debe instalarse en el cliente.

📂 Ficheros generados

ModConfig/AxinMenuGUI/
├── config.json             # Configuración global (idioma, killPoints, ranking, RTP, market)
├── adminteleports.json     # Puntos ATP guardados con /atp set (auto-generado)
├── menus/                  # Tus ficheros JSON de menú
│   ├── ejemplo.json        # Menú índice con submenús (/menu)
│   ├── ejemplo_jugador.json
│   ├── ejemplo_stats.json
│   ├── ejemplo_temas.json  # 13 escenas, un tema por pestaña (/temas)
│   ├── ejemplo_tp.json     # Panel de teleport admin (ATP, RTP, waypoints)
│   └── ...
├── playerdata/             # Stats por jugador (auto-generado)
│   └── {uid}.json
├── bank/                   # Snapshots de Banks por jugador (auto-generado)
│   └── {uid}.json
├── missions/               # Configuración de misiones (auto-generado)
│   └── killranking.json
└── marketlog/              # Log persistente de transacciones de mercado (auto-generado)
    └── market_log_{fecha}.json

🎯 Tu primer menú

hola.json{
  "id": "hola",
  "title": "Mi primer menú",
  "theme": "dark-red",
  "rows": 3,
  "commandAlias": "hola",
  "scenes": {
    "0": {
      "items": {
        "saludo": {
          "slot": 13,
          "item": "axinmenugui:menuicon-star",
          "name": "Hola, {player}!",
          "lore": ["Clic para saludar"],
          "clickEvents": {
            "msg": { "type": "message", "message": "¡Bienvenido, {player}!" }
          }
        }
      }
    }
  }
}

Guarda el fichero en ModConfig/AxinMenuGUI/menus/ y ejecuta /amenu reload. Luego /hola para abrir el menú.

📋 Comandos y permisos

Todos los comandos disponibles en AxinMenuGUI.

ComandoPermisoDescripción
/amenu open <id> [jugador]chatAbre un menú. Con jugador: requiere controlserver.
/amenu reloadcontrolserverRecarga todos los JSONs y re-registra alias.
/amenu listcontrolserverLista los menús cargados.
/amenu info <id>controlserverDetalle de un menú.
/amenu player <nombre>controlserverStats del jugador + ranking.
/amenu rankingchatMuestra el ranking público.
/amenu click open <id>controlserverModo vinculación: el siguiente clic derecho en un bloque lo vincula al menú indicado.
/amenu click deletecontrolserverModo desvinculación: el siguiente clic derecho en un bloque elimina su vínculo de menú.
/<alias>chatAbre el menú con ese commandAlias.
/amenu click — flujo de dos pasos

Tras ejecutar /amenu click open <id> o /amenu click delete, el jugador entra en modo de selección. El siguiente clic derecho sobre cualquier bloque confirma la operación. Los vínculos se guardan en ModConfig/AxinMenuGUI/menusClick.json.

🏪 Mercado global (/market)

El mercado P2P permite a los jugadores publicar y comprar ofertas. Todos los subcomandos requieren privilegio chat.

ComandoDescripción
/marketAlias directo: abre el menú principal del mercado.
/amenu marketÍdem: abre el menú principal del mercado.
/amenu market buyAbre la vista de compra (explorar y comprar ofertas).
/amenu market browse [página]Lista ofertas agrupadas por tipo de ítem (paginada).
/amenu market detail <itemCode>Muestra todos los vendedores de un ítem concreto con precio y cantidad.
/amenu market sellSin argumentos: abre la GUI de venta personalizada (wizard interactivo).
/amenu market sell <item> <cant> <precioItem> <precioCant>Venta rápida: publica la oferta directamente con todos los parámetros.
/amenu market cancel <offerId>Cancela tu oferta activa; devuelve los ítems restantes al inventario.
/amenu market myoffersMuestra tu lista de ofertas activas.
/amenu market collectCobra las ganancias pendientes generadas por ventas completadas.
/amenu market listLista todas las ofertas activas en formato texto (chat).
💡
Wizard de venta personalizada

Al ejecutar /amenu market sell sin argumentos, se inicia un asistente paso a paso por chat: (1) selecciona qué ítem vender del inventario en la GUI, (2) escribe o selecciona el ítem de pago, (3) escribe la cantidad de pago por unidad. Escribe cancel en cualquier momento para salir. El wizard expira por inactividad según marketSellWizardTimeoutSeconds en config.json.

⚡ Teleport administrativo (ATP)

Gestiona puntos de teletransporte guardados en el servidor. Todos requieren privilegio controlserver.

ComandoDescripción
/atp set <nombre>Guarda tu posición actual como punto ATP con ese nombre.
/atp setat <nombre> <x> <y> <z>Guarda un ATP en coordenadas explícitas.
/atp go <nombre>Teleport al punto ATP guardado.
/atp del <nombre>Elimina un punto ATP.
/atp listLista todos los puntos ATP guardados.
/atp info <nombre>Muestra coordenadas y metadatos del punto ATP.

🌀 Teletransporte aleatorio (RTP)

Teletransporta al jugador a una posición aleatoria segura. Requiere privilegio controlserver.

ComandoDescripción
/artpRTP con radio mínimo y máximo definidos en config.json.
/artp <min> <max>RTP con radio explícito. Acepta enteros o sufijo k (ej. /artp 5k 10k).
Limitaciones del RTP

La verificación de claims solo comprueba el punto exacto de aterrizaje (limitación de la API de VintageStory). Las story zones se aproximan por distancia al spawn. Verificado en mundo plano; se recomienda prueba adicional en mundos con relieve.

Output de /amenu player

[AxinMenuGUI] Stats: elYandrack
  timeRealSeconds:       77  (1m 17s)
  timeGameDays:          0.00
  deaths:                0
  mobKillsAll:           4
  mobKillsHostile:       3
  mobKillsHostilePoints: 22.5
  playerKills:           0
  ranking.position:      #1

Configuración global

El fichero config.json se genera automáticamente al primer inicio.

ModConfig/AxinMenuGUI/config.json{
  "language": "en",
  "rankingSize": 10,
  "rankingField": "mobKillsHostilePoints",
  "killPoints": {
    "drifter-normal":        0.5,
    "drifter-deep":          2.0,
    "drifter-tainted":       4.0,
    "drifter-corrupt":       8.0,
    "drifter-nightmare":     14.0,
    "drifter-double-headed": 20.0
    // ... 29 mobs en total
  },
  "randomTeleport": {
    "defaultMin":          5000,
    "defaultMax":          10000,
    "maxAttempts":         40,
    "claimMargin":         128,
    "storyZoneMargin":     256,
    "forbidWater":         true,
    "requireTwoAirBlocks": true
  },
  "saveIntervalSeconds":            30,
  "rankingCacheSeconds":            60,
  "marketLogRetentionDays":         30,
  "marketLogFlushSeconds":          30,
  "marketSellWizardTimeoutSeconds":  60
}
CampoDefaultDescripción
languageenIdioma del mod: en, es, fr, de, pt-br, ru
rankingSize10Número de jugadores en el ranking
rankingFieldmobKillsHostilePointsCampo por el que se ordena: mobKillsHostilePoints, deaths, playerKills
saveIntervalSeconds30Intervalo entre flush automático de player stats a disco. Mínimo 5.
rankingCacheSeconds60Duración del caché de ranking en segundos.
killPointsPuntos por tipo de mob hostil. Lookup progresivo: wolf-male-adultwolf-malewolf.

🏪 Bloque market

CampoDefaultDescripción
marketLogRetentionDays30Días de histórico de marketlog/ a conservar. Los archivos más antiguos se purgan automáticamente.
marketLogFlushSeconds30Intervalo en segundos entre flush de la cola de log de mercado a disco. Mínimo 5.
marketSellWizardTimeoutSeconds60Segundos de inactividad antes de cancelar un wizard de venta personalizada activo.

🌀 Bloque randomTeleport

Configuración del teletransporte aleatorio (/artp y click event randomTeleport).

CampoDefaultDescripción
randomTeleport.defaultMin5000Radio mínimo por defecto al usar /artp sin argumentos.
randomTeleport.defaultMax10000Radio máximo por defecto al usar /artp sin argumentos.
randomTeleport.maxAttempts40Intentos máximos antes de cancelar la búsqueda de posición segura.
randomTeleport.claimMargin128Margen de claims. Limitación de API: solo se comprueba el punto exacto de aterrizaje, no el perímetro.
randomTeleport.storyZoneMargin256Distancia mínima al spawn como proxy de story zones. No equivale a detección real de zonas de historia.
randomTeleport.forbidWatertrueRechaza agua u otros líquidos como suelo de aterrizaje.
randomTeleport.requireTwoAirBlockstrueExige dos bloques de aire libres (pies y cabeza) en el punto de destino.

🖼 Estructura del GUI

Anatomía completa de un menú JSON.

mi_menu.json{
  "id": "mi_menu",
  "title": "Mi Menú",
  "theme": "dark-red",
  "rows": 3,
  "commandAlias": "menu",
  "commandAliasTarget": "OPTIONAL",
  "permission": "",
  "openTriggers": [],
  "scenes": {
    "0": {
      "delay": 0,
      "theme": "parchment",
      "items": {
        "boton1": {
          "slot": 13,
          "item": "axinmenugui:menuicon-star",
          "amount": 1,
          "name": "Hola, {player}!",
          "lore": ["Primera línea", "Segunda línea"],
          "hideOnFail": false,
          "clickEvents": {
            "ev1": { "type": "message", "message": "¡Hola!" }
          },
          "conditions": {},
          "conditionFailMessage": ""
        }
      }
    }
  }
}
Campo del menúTipoDescripción
idstringID único del menú (requerido)
titlestringTítulo mostrado en la barra del GUI
themestringTema visual del menú (ver sección Temas)
rowsintFilas del grid (1–6)
commandAliasstringAlias de comando para abrir el menú
commandAliasTargetstringOPTIONAL · REQUIRED · DISABLED
permissionstringPrivilegio VS requerido (vacío = todos)
Campo del ítemTipoDescripción
slotintPosición 0-indexed (9 columnas)
itemstringCódigo VS o axinmenugui:menuicon-*
amountintCantidad visual
namestringNombre. Admite tokens y VTML.
lorestring[]Tooltip. Admite tokens y VTML.
hideOnFailbooltrue = ocultar si condición falla

📄 Escenas y navegación

Un menú puede tener múltiples escenas (páginas). Cada escena puede tener su propio tema visual.

🗂 Múltiples escenas

"scenes": {
  "0": { "theme": "dark-red",  "items": { ... } },
  "1": { "theme": "dark-blue", "items": { ... } },
  "2": { /* sin theme → hereda el del menú */ "items": { ... } }
}

🧭 Click events de navegación

TypeDescripción
nextSceneAvanza a la escena siguiente
previousSceneRetrocede a la escena anterior
openGuiAbre otro menú (submenú). Se registra en el historial.
backVuelve al menú anterior del historial.
closeGuiCierra el menú activo.

🔗 Patrón submenús con back

// En el menú índice:
"sub_stats": {
  "slot": 13,
  "item": "axinmenugui:menuicon-trophy",
  "name": "Estadísticas",
  "clickEvents": {
    "open": { "type": "openGui", "guiId": "ejemplo_stats" }
  }
}

// En ejemplo_stats.json:
"volver": {
  "slot": 17,
  "item": "axinmenugui:menuicon-back",
  "name": "← Volver",
  "clickEvents": {
    "back": { "type": "back" }
  }
}

🎨 Temas visuales

14 temas Cairo para personalizar completamente el aspecto de tus menús.

🖼 Cómo aplicar un tema

Añade el campo "theme" al menú y/o a cada escena:

{
  "id": "mi_menu",
  "theme": "dark-red",    // tema por defecto del menú
  "scenes": {
    "0": { "theme": "parchment", "items": { ... } },  // sobreescribe el del menú
    "1": { "items": { ... } }                         // hereda "dark-red"
  }
}
💡
El menú ejemplo_temas.json tiene 13 escenas, una por tema. Abre con /temas para ver todos en acción.

🎨 Temas disponibles (14)

default
Aspecto estándar VS
dark-red
Rojo oscuro, esquinas redondeadas
dark-blue
Azul oscuro marino
dark-green
Verde bosque oscuro
parchment
Pergamino dorado
stone
Piedra gris
night
Noche violeta
dark-red-2 ⬛
Rojo sangre, marco cofre 7px
dark-blue-2 ⬛
Azul océano, marco cofre 7px
dark-green-2 ⬛
Verde bosque, marco cofre 7px
parchment-2 ⬛
Madera oscura, marco cofre 8px
stone-2 ⬛
Granito oscuro, marco cofre 7px
night-2 ⬛
Void negro, marco violeta 8px
glass 🔮
Panel transparente, título flotante

📐 Diferencias entre variantes

VarianteBorderWCornerREfecto
Original (dark-red, etc.)4px8pxMarco fino, esquinas redondeadas
Cofre (dark-red-2, etc.)7–8px2pxMarco grueso con bisel automático, esquinas cuadradas. Estilo cofre VS.
glass1px6pxCompletamente transparente. Solo cuadrícula y título visibles.
Los temas -2 activan automáticamente un bisel (highlight de borde) cuando el mod detecta BorderW > 4. Este efecto no requiere configuración adicional.

🖱 Click Events

Acciones que se ejecutan al hacer clic en un botón del menú.

TypeEstadoDescripción
message✅ RUNEnvía un mensaje al jugador. Admite tokens.
closeGui✅ RUNCierra el menú activo.
openGui✅ RUNAbre otro menú. Guarda el menú actual en historial.
back✅ RUNVuelve al menú anterior del historial.
nextScene✅ RUNAvanza a la escena siguiente.
previousScene✅ RUNRetrocede a la escena anterior.
playerCommand✅ RUNEjecuta un comando como el jugador.
consoleCommand✅ RUNEjecuta un comando de servidor (sin límite de permisos).
giveItem✅ RUNDa ítems al jugador.
takeItem✅ IMPLQuita ítems del inventario.
buyItem✅ IMPLCompra: verifica coste + consume + entrega.
sellItem✅ IMPLVenta: verifica ítem + consume + entrega currency.
setVariable✅ IMPLModifica un field del jugador (set/add/subtract/multiply/divide).
teleport✅ RUNTeletransporta al jugador a coordenadas fijas.
teleportSaved✅ RUNTeleport a un punto ATP guardado por nombre.
randomTeleport✅ RUNTeletransporte aleatorio seguro con validación de terreno.

Ejemplos de uso

// message
"ev": { "type": "message", "message": "Hola {player}, estás en {pos}" }

// openGui (submenú)
"ev": { "type": "openGui", "guiId": "mi_submenu" }

// back
"ev": { "type": "back" }

// consoleCommand
"ev": { "type": "consoleCommand", "commands": ["/tp {player} 0 150 0"] }

// giveItem
"ev": { "type": "giveItem", "itemCode": "game:ingot-iron", "quantity": 5 }

// setVariable
"ev": { "type": "setVariable", "field": "puntos", "op": "add", "value": 10 }

// teleport — coordenadas fijas separadas por coma
"ev": { "type": "teleport", "location": "0,150,0" }

// teleportSaved — usa un punto ATP guardado con /atp set
"ev": { "type": "teleportSaved", "target": "spawn" }

// randomTeleport — radios explícitos (0,0 usa defaults de config.json)
"ev": { "type": "randomTeleport", "radiusMin": 1000, "radiusMax": 5000 }

Condiciones

Muestra u oculta botones según el estado del jugador.

TypeEstadoParámetros
hasPrivilege✅ RUNprivilege: string
hasPrivilegeLevel✅ IMPLminLevel: int
hasItem✅ IMPLitemCode, quantity
hasRole✅ IMPLroleCode: string
playerDataCompare✅ IMPLfield, op, value
cooldownActive✅ IMPLid, duration
"conditions": {
  "es-admin": { "type": "hasPrivilege", "privilege": "controlserver", "inverted": false }
},
"hideOnFail": true,
"conditionFailMessage": "Solo administradores."

🖼 Iconos del mod

58 iconos pixel art 32×32 incluidos en AxinMenuGUI. Úsalos en cualquier botón de tus menús.

Cómo usarlos

En el campo "item" de cualquier ítem, usa el prefijo axinmenugui:menuicon- seguido del nombre del icono.

Ejemplo: "item": "axinmenugui:menuicon-star"

🧭 Navegación

ℹ UI / Información

🛒 Tienda / Economía

👤 Social / Admin / Roles

✨ Decorativos / Misc

💡 Cómo añadir iconos propios

Puedes añadir tus propios iconos al mod creando los ficheros necesarios:

  1. Coloca el PNG (32×32, fondo transparente, estilo pixel art) en:
    assets/axinmenugui/textures/item/menuicon-MINOMBRE.png
  2. Crea el fichero de itemtype en:
    assets/axinmenugui/itemtypes/menuicon-MINOMBRE.json
  3. El contenido del JSON del itemtype es:
menuicon-MINOMBRE.json{
  "code": "menuicon-MINOMBRE",
  "class": "Item",
  "maxStackSize": 1,
  "texture": { "base": "axinmenugui:item/menuicon-MINOMBRE" }
}
🚨
El campo texture es OBLIGATORIO

VintageStory no infiere la textura desde el código del ítem. Sin este campo el slot aparece negro aunque el PNG exista.

  1. Reinicia el servidor. Usa el icono con: "item": "axinmenugui:menuicon-MINOMBRE"

💾 Player Data y Stats

Variables persistentes por jugador: stats automáticos, campos personalizados, cooldowns.

📊 Stats automáticos

El mod trackea automáticamente al entrar, morir y matar entidades:

CampoDescripción
deathsNúmero de veces que ha muerto
mobKillsAllTotal de mobs asesinados
mobKillsHostileMobs hostiles asesinados
mobKillsHostilePointsPuntos acumulados (escala en config.json)
playerKillsJugadores asesinados
timeRealSecondsTiempo real en el servidor (segundos)
timeGameDaysDías de juego vividos

🎯 Puntos por mob

Los puntos se calculan con lookup progresivo: wolf-male-adult → prueba wolf-male → prueba wolf. Configúralos en config.json.

📋 Ranking

El ranking se calcula automáticamente sobre el campo rankingField (por defecto mobKillsHostilePoints). Se refresca con caché de 30 segundos.

🔗 Tokens / Placeholders

Variables dinámicas que puedes usar en cualquier campo de texto del menú.

TokenDescripción
{player}Nombre del jugador
{uid}UID único del jugador
{pos}Posición X,Y,Z actual
{gamemode}Modo de juego actual
{world}Nombre del mundo
{var:campo}Field personalizado del jugador
{stats.deaths}Número de muertes
{stats.mobKillsAll}Total mobs asesinados
{stats.mobKillsHostile}Mobs hostiles asesinados
{stats.mobKillsHostilePoints}Puntos por kills hostiles
{stats.playerKills}Jugadores asesinados
{stats.timeReal}Tiempo real formateado (Xh Ym)
{stats.timeGame}Tiempo in-game (X.X días)
{ranking.position}Posición en el ranking
{ranking.value}Valor del campo de ranking
{ranking.top}Ranking completo formateado
{ranking.top3}Top 3 formateado
{ranking.topN}Top N formateado
💡
Los tokens se resuelven en el servidor antes de enviar el menú al cliente, por lo que son visibles directamente en el nombre y lore de los botones.

💬 Chat Fetcher

Solicita input al jugador vía chat. Fase 3

Chat Fetcher está planificado para la Fase 3 y aún no está implementado.

Modos de apertura

Formas de abrir un menú en AxinMenuGUI.

TriggerEstadoDescripción
/amenu open <id>✅ RUNComando directo de admin.
commandAlias✅ RUNAlias por menú: /menu, /temas, etc.
Block Click Trigger✅ RUNClic derecho en un bloque vinculado. Configuración guardada en menusClick.json.
Menús ATP/RTP✅ RUNMenús con click events de teleport o ATP, accesibles por alias propio.
Ítem especialFase 2 — pendienteClic derecho en un ítem configurado (Bloque 2.7).
HotkeyFase 3Tecla configurable (ClientSide).
onJoinFase 3Al conectarse al servidor.
claimZoneFase 3 (opcional)Al entrar en una zona con flag.

📦 Menús de ejemplo incluidos

8 menús se generan automáticamente al instalar el mod.

💡
Los menús de ejemplo se actualizan automáticamente

A partir de la v0.7.2, los menús de ejemplo oficiales se sobreescriben en cada recarga del mod para mantenerse al día. Tus menús propios (con IDs distintos) nunca se ven afectados.

IDAliasDescripción
ejemplo/menuÍndice con submenús navegables (openGui + back). Incluye acceso al panel TP si eres admin.
ejemplo_jugadorDatos del jugador: nombre, pos, modo, UID
ejemplo_statsEstadísticas completas + ranking
ejemplo_tiendaTienda demo con giveItem (solo admins)
ejemplo_adminPanel admin: reload, list, info, open
ejemplo_temas/temas13 escenas, 1 por tema visual. Todo en 1 JSON.
ejemplo_multitema/multitemaDemo de theme por escena (7 temas en 1 menú)
ejemplo_tpPanel de teleport admin: lista ATP, RTP con distintos radios, waypoints personales (solo admins)

Navegación del menú principal

/menu → ejemplo.json (índice)
    ├── Sobre ti     → ejemplo_jugador.json  [← Volver]
    ├── Estadísticas → ejemplo_stats.json    [← Volver]
    ├── Tienda       → ejemplo_tienda.json
    ├── Panel Admin  → ejemplo_admin.json    (oculto si no es admin)
    └── Teleport TP  → ejemplo_tp.json       (oculto si no es admin)

🗺 Roadmap

Plan de desarrollo por fases del mod.

1
Fase 1 — MVP ✅ CERRADA
✅ Estructura + modinfo · ✅ Carga menús JSON · ✅ GUI grid posicionado · ✅ Click events: message, closeGui, openGui, nextScene, previousScene, back, consoleCommand, playerCommand · ✅ commandAlias · ✅ 58 iconos personalizados · ✅ 8 menús de ejemplo embebidos · ✅ Block Click Triggers · ✅ Distribución cliente verificada ingame
2
Fase 2 — Acciones y condiciones ✅ CERRADA
✅ giveItem/takeItem/buyItem/sellItem · ✅ setVariable (matemáticas) · ✅ Condiciones: hasPrivilege, hasItem, hasRole, cooldownActive · ✅ hideOnFail · ✅ PlayerStats (kills, tiempo, muertes, ranking) · ✅ 14 temas Cairo (glass y variantes cofre) · ✅ Theme por escena · ✅ Click event teleport (coordenadas) · ✅ ATP: /atp set|setat|go|del|list|info y click event teleportSaved · ✅ RTP: /artp y click event randomTeleport
2.5
Fase 2.5 — Economía y misiones ✅ CERRADA · v0.9.5
✅ Mercado Global P2P: publicar ofertas con escrow, comprar, cancelar, cobrar ganancias · ✅ Venta rápida por comando · ✅ Wizard de venta personalizada (chat interactivo, timeout configurable) · ✅ Log persistente de transacciones (rotación diaria, retención configurable) · ✅ Bank: bloque crafteable como fuente de pago secundaria · ✅ Integración Treasury + Bank en CanAfford/PayFromBuyer · ✅ KillRanking Missions: hitos de puntos con recompensa de ítem, mensaje, broadcast y comando · ✅ Migración automática de config.json (v0.4)
3
Fase 3 — Avanzado
ChatFetcher · PlayerPicker · Trigger por ítem especial · Trigger onJoin · Trigger hotkey · Trigger claimZone (integración AxinClaimsRules, opcional) · Sound events · Title events · Condición playerDataCompare
4
Fase 4 — Editor en juego v1.0.0
Interfaz visual de creación y edición de menús directamente en el juego · Vista previa en tiempo real · Generación automática de JSON

AxinMenuGUI vs GUIPlus

Diferencias y equivalencias entre los dos sistemas.

GUIPlus (Minecraft)AxinMenuGUI (VintageStory)Nota
YAMLJSONEstándar en VS
Inventario ChestGuiDialog con grid configurableVS no tiene chest como GUI nativa
Temas de color limitados14 temas Cairo personalizables✅ Sistema propio completo
§ color codesVTML — <font color="#FF8C00">texto</font>✅ Verificado en GuiDialog
Vault / economyPlayer Data numérico + statsNo hay economy nativa en VS
PlaceholderAPISistema de tokens {token}18+ tokens incluyendo stats y ranking
LuckPermsRoles nativos VS (hasRole, hasPrivilegeLevel)API nativa VS
Skull textures58 iconos pixel art propiosDominio axinmenugui:
BungeeCordNo aplicaVS es standalone
In-game editorFase 4Primero config 100% por JSON

🏪 Mercado Global P2P

Permite a los jugadores publicar ofertas de venta y comprar ítems de otros jugadores. Las transacciones usan escrow automático.

🚀 Cómo acceder

Usa el comando /market o /amenu market para abrir el menú principal. No requiere permisos especiales.

🛒 Flujo de compra

  1. Abre el mercado con /market.
  2. Selecciona Comprar para ver ítems disponibles agrupados por tipo.
  3. Selecciona un ítem para ver los vendedores, precio y cantidad disponible.
  4. Haz clic en la oferta que quieras comprar. El sistema verificará que puedes pagar (inventario + Treasury + Bank cerrados).
  5. Si la compra prospera, los ítems aparecen en tu inventario y el vendedor recibe una notificación.
Fuentes de pago automáticas

El sistema cobra en este orden: (1) inventario del jugador, (2) Treasury cerrada, (3) Banks cerrados. No es necesario tener el pago en el inventario si tienes saldo en Treasury o Bank.

💰 Flujo de venta — venta rápida

Para publicar una oferta directamente sin GUI:

/amenu market sell <itemCode> <cantidad> <precioItem> <precioCantidad>

Ejemplo: vender 10 lingotes de hierro a 2 engranajes oxidados cada uno
/amenu market sell game:ingot-iron 10 game:gear-rusty 2

Los ítems se retiran inmediatamente del inventario y quedan en escrow hasta ser comprados o cancelados.

🧙 Flujo de venta — asistente personalizado

Para iniciar el wizard interactivo, ejecuta /amenu market sell sin argumentos. Se abre la GUI de tu inventario y el proceso es guiado por chat:

  1. Haz clic en el ítem de tu inventario que quieres vender.
  2. Si tienes más de 1 unidad, escribe por chat la cantidad a vender.
  3. Selecciona en la GUI (o escribe por chat en formato dominio:codigo) el ítem que pides como precio.
  4. Escribe por chat la cantidad de ese ítem que pides por cada unidad vendida.
  5. La oferta se publica y los ítems pasan a escrow.
Escribe cancel para salir

En cualquier paso del wizard puedes escribir cancel por chat para cancelar. El wizard también expira automáticamente por inactividad según marketSellWizardTimeoutSeconds en config.json (default 60s).

📋 Gestión de ofertas

AcciónComando
Ver mis ofertas activas/amenu market myoffers
Cancelar una oferta/amenu market cancel <offerId> — devuelve ítems restantes
Cobrar ganancias pendientes/amenu market collect
Listar todas las ofertas (texto)/amenu market list
💡
Ganancias pendientes

Cuando alguien compra tu oferta, el pago queda en cola hasta que uses /amenu market collect. El vendedor recibe una notificación in-game si está conectado.

📦 Estado de una oferta

EstadoSignificado
activeLa oferta está disponible para compra.
soldToda la cantidad fue comprada.
cancelledCancelada por el vendedor; ítems devueltos.

📝 Log de transacciones

Todas las transacciones quedan registradas en ModConfig/AxinMenuGUI/marketlog/. Los archivos se rotan diariamente y se purgan automáticamente pasados marketLogRetentionDays días (default 30).

Acción en logDescripción
SELL_PUBLISHEDOferta publicada por un vendedor.
SELL_FILLEDParte o toda la oferta fue comprada (par con BUY_FILLED, mismo TxId).
BUY_FILLEDCompra realizada por un comprador.
OFFER_CANCELLEDOferta cancelada por el vendedor.
EARNINGS_COLLECTEDVendedor cobró sus ganancias pendientes.

🏦 Bank (bloque)

Cofre especial crafteable que actúa como fuente de pago secundaria en el Mercado Global.

📐 Qué es el Bank

El Bank es un bloque de almacenamiento similar a un cofre. La diferencia clave es que el Mercado Global usa automáticamente los ítems del Bank como fuente de pago cuando el inventario del jugador no es suficiente, siempre que el Bank esté cerrado en el momento de la transacción.

Comportamiento open/closed

Un Bank abierto (interfaz activa) no se usa como fuente de pago. Solo los Banks cerrados contribuyen al saldo disponible para compras en el mercado. El snapshot se sincroniza 5 segundos después de cerrar el inventario.

🔨 Crafteo

El Bank se craftea con planchas de madera (cualquier tipo), clavos metálicos y piedra tallada. La receta exacta está disponible en el Handbook del juego buscando "Bank".

🏗 Colocación y uso

  1. Coloca el Bank en el mundo como cualquier cofre.
  2. Ábrelo con clic derecho para guardar ítems.
  3. Al cerrarlo, el servidor sincroniza el contenido en ModConfig/AxinMenuGUI/bank/{uid}.json.
  4. Cuando realices una compra en el mercado, el sistema sumará automáticamente el saldo de todos tus Banks cerrados al calcular si puedes pagar.

📊 Múltiples Banks

Un jugador puede tener varios Banks colocados. El servicio los identifica por su posición en el mundo. Todos los Banks cerrados de un mismo jugador contribuyen al saldo total disponible para el mercado.

🗂 Persistencia

Los snapshots se guardan en ModConfig/AxinMenuGUI/bank/{uid}.json, uno por jugador, con la lista de Banks y su contenido en el momento del último cierre.

🎯 KillRanking Missions

Recompensa automática y única cuando un jugador cruza un umbral de puntos de ranking de kills hostiles.

📄 Fichero de configuración

Se genera automáticamente en ModConfig/AxinMenuGUI/missions/killranking.json con valores de ejemplo. Se recarga con /amenu reload.

missions/killranking.json{
  "DocVersion": "0.1",
  "RankingBonus": {
    "10": {
      "message":     "¡Has alcanzado 10 puntos de ranking!",
      "all_message": "{player} ha alcanzado 10 puntos de ranking.",
      "give": { "type": "item", "code": "game:gear-rusty", "amount": 1 },
      "command":     "/amenu ranking"
    },
    "50": {
      "message":     "¡Leyenda! 50 puntos de ranking.",
      "all_message": "{player} ha alcanzado 50 puntos de ranking.",
      "give": { "type": "item", "code": "game:ingot-iron", "amount": 5 },
      "command":     null
    }
  }
}

📋 Campos de un hito

CampoTipoDescripción
Clave (número)doubleUmbral de mobKillsHostilePoints que activa el hito.
messagestringMensaje privado al jugador. Admite placeholders {player}, {stats.*}, {ranking.*}.
all_messagestringMensaje broadcast a todos los jugadores conectados. Admite los mismos placeholders.
give.typestringTipo de recompensa. Por ahora solo "item".
give.codestringCódigo del ítem a entregar (e.g. game:gear-rusty).
give.amountintCantidad del ítem a entregar.
commandstringComando de servidor a ejecutar cuando se cruza el hito. Admite placeholders. null = ninguno.

⚙ Comportamiento

  • Cada hito se concede exactamente una vez por jugador, aunque los puntos bajen y vuelvan a subir.
  • La verificación ocurre en el momento del kill, sin I/O adicional (todo en memoria).
  • Los umbrales se evalúan de menor a mayor; si un jugador sube varios umbrales de golpe, recibe todos.
  • Las recompensas no concedidas (give: null) se omiten sin error.
  • El estado "ya concedido" persiste en playerdata/{uid}.json y sobrevive a reloads y reinicios.
💡
Recarga en caliente

Puedes añadir nuevos umbrales o modificar las recompensas y ejecutar /amenu reload sin reiniciar el servidor. Los hitos ya concedidos no se revierten.