Inventario y equipo
Un inventario que el jugador puede abrir y usar: los objetos que da la historia aparecen en una mochila, las pociones se pueden beber (sube la salud y baja la cuenta) y un arma se puede equipar para que la IA sepa qué lleva el jugador en la mano. La mochila, la poción y el arma se pueden montar sin código; una cuadrícula completa de objetos con botones para cada uno es la ruta con código propio del final.
Lo que vas a construir
- Una mochila que el jugador abre desde el chat: todo lo que ha ido recogiendo, cada cosa con una frase de descripción, y un aviso cuando la historia le da algo nuevo
- Un botón de poción: Salud +20, una poción menos y «¡No te quedan pociones!» cuando ya no hay
- Un botón para equipar: la ranura del arma pone «Espada de hierro» y la IA describe al jugador empuñándola
- La IA mantiene la mochila al día según avanza la historia: botín, recompensas, cosas perdidas o rotas
Cómo se reparten las piezas
| Qué | Quién se encarga | Por qué |
|---|---|---|
| La lista de objetos recogidos en la historia | La IA, con las Reglas de comportamiento de la variable de la mochila | Solo la IA sabe cuándo la historia le da algo al jugador |
| Contar pociones y curar | Un comportamiento | Las cuentas tienen que ser exactas |
| El arma equipada | Un botón o un comportamiento | Un solo valor, que se pone cuando el jugador elige |
| Mostrarlo todo | La interfaz del jugador | Plantillas y partes, sin código |
Paso a paso
Paso 1: Añade la plantilla Inventario
Pulsa Interfaz en el centro de la barra superior, luego Plantillas, y añade Inventario (en Durante la partida · se abre desde el Menú del chat): «Toca un objeto para usarlo; aparece un aviso cuando la historia te da algo.»
Añade una página de mochila, una ventana emergente en la página del chat y dos variables:
| Variable | Qué es |
|---|---|
| Inventario (Lista / tabla) | Una fila por objeto, cada una con un nombre y una nota de una línea |
| Recién conseguido (Texto) | Lo que el jugador acaba de conseguir; la ventana emergente lo muestra y lo vacía al cerrarse |
Las dos traen Reglas de comportamiento que le explican a la IA cómo usarlas. Las de Inventario dicen, entre otras cosas:
Cuando el jugador consiga algo, haz push y en el mismo turno pon new_item en "objeto — una línea". Si se gasta o se pierde, borra su índice (desde 0). No repartas objetos cada turno.
La IA las sigue en la historia, así que el botín, los regalos y las recompensas acaban en la mochila sin que tengas que hacer nada.
Al tocar un objeto en la página de la mochila se envía una frase por el jugador («(Saco Reloj de bolsillo)») y se vuelve al chat, para que la IA narre cómo lo usa.
Los objetos iniciales van en el Valor inicial de la variable Inventario, por ejemplo:
[{"name": "Espada de hierro", "note": "Sencilla, pero bien equilibrada."}]Paso 2: Pociones que se cuentan
La mochila es una lista que escribe la IA. Para lo que necesita números exactos, como cuántas pociones quedan, usa una variable de Número y un comportamiento.
Variables (en la fila Puedes añadir, + Variable):
| Nombre | Tipo | Valor inicial | Rango | Notas |
|---|---|---|---|---|
| Salud | Número | 80 | de 0 a 100 | Reglas de comportamiento: Baja cuando el jugador recibe daño; se recupera poco a poco al descansar. |
| Pociones | Número | 2 | de 0 a (vacío) | Reglas de comportamiento: Cuántas pociones de curación lleva el jugador. Suma cuando encuentre o compre una. |
Empezar con la Salud por debajo del máximo le da al jugador un motivo para probar una poción.
Comportamiento (+ Comportamiento): «Beber una poción»
| Parte | Ajuste |
|---|---|
| Cuándo se dispara | El jugador pulsa un botón, Botón drink-potion |
| SOLO SI | Pociones es mayor que 0 |
| Efectos | Cambiar variable: Pociones menos 1 |
Cambiar variable: Salud más 20 | |
Mostrar notificación: Te bebes una poción. Salud +20, estilo Éxito | |
Decirle a la IA: El jugador acaba de beberse una poción de curación. | |
| Si no se cumplen las condiciones, decir | ¡No te quedan pociones! |
La Salud no puede pasar de 100: el rango de la variable la limita.
Botón: en Interfaz, en la página de la conversación o en la de la mochila, añade un Botón con el texto «Beber una poción (quedan {Pociones})» (usa Insertar el valor de una variable… para la cuenta). En Al pulsar, en orden, añade Activar un comportamiento → Beber una poción.
Paso 3: Equipa un arma
Variable: Arma equipada, de Texto, vacía al empezar. Reglas de comportamiento:
El arma que lleva el jugador en la mano; si está vacío, va con las manos desnudas. Describe los combates con ella. Si en la historia se rompe o se la quitan, vuelve a dejarla vacía.Botón: «Equipar Espada de hierro», con dos pasos:
- Cambiar una variable: Arma equipada, Poner en,
Espada de hierro - Mostrar un aviso:
Has equipado la Espada de hierro
Dale Cuándo mostrar → Cuando una variable cumple: Arma equipada no es Espada de hierro, para que el botón desaparezca cuando ya esté equipada. Al lado, una parte Texto con Arma: {Arma equipada} muestra la ranura.
La IA ve Arma equipada en su prompt en cada turno, así que en su siguiente respuesta el jugador ya lleva la espada. Si quieres que lo note al instante, haz que el botón haga Activar un comportamiento que cambie la variable y añada Decirle a la IA El jugador acaba de equiparse la Espada de hierro.
Paso 4: Pruébalo
Pulsa Jugar en la barra superior.
- La Salud está en 80, Pociones en 2 y la ranura del arma vacía
- Pulsa Beber una poción: Salud 100, Pociones 1, y sale un aviso
- Púlsalo dos veces más: la segunda vez sale «¡No te quedan pociones!» y no cambia nada
- Pulsa Equipar Espada de hierro: la ranura pone Espada de hierro y el botón desaparece. Empieza una pelea; la IA mete la espada en ella
- Juega hasta que la historia te dé algo: lo muestra la ventana emergente y está en la mochila (Menú → Mochila)
Si algo falla:
| Síntoma | Causa probable | Solución |
|---|---|---|
| El botón de poción no hace nada | El comportamiento del botón y el nombre de Botón del comportamiento no coinciden | Activar un comportamiento tiene que apuntar a Beber una poción |
| Las Pociones se quedan en negativo | Falta la condición de SOLO SI | Añade Pociones es mayor que 0 |
| La Salud pasa de 100 | La Salud no tiene máximo | Pon su Rango de 0 a 100 |
| Los objetos nunca aparecen en la mochila | La IA no está escribiendo en Inventario | Comprueba que la variable Inventario sigue teniendo sus Reglas de comportamiento y que Acceso de la IA está en La IA puede leer y escribir |
| La ventana emergente no sale nunca | La IA actualiza Inventario pero no Recién conseguido | Deja las Reglas de comportamiento de Recién conseguido; piden las dos cosas en el mismo turno |
Por qué aquí los comportamientos no pueden con todo
Los efectos de un comportamiento cambian números y texto (fijar, sumar, restar, multiplicar, alternar, añadir texto al final). No pueden añadir una fila a una lista, encontrar «la poción» dentro de ella ni quitarla. Las condiciones pueden comprobar si una lista contiene un valor simple, pero no mirar dentro de las filas. Por eso esta receta deja la lista de objetos en manos de la IA y cuenta lo que necesita números exactos en sus propias variables de Número.
El efecto Código de un comportamiento sí puede añadir a una lista si escribes una línea de JavaScript, por ejemplo ctx.push("Inventory", { name: "Potion", note: "Heals a little." }). Mira Comportamientos · Comportamientos con código.
Si quieres las dos cosas en el mismo sitio (una cuadrícula de objetos de verdad, donde cada uno tiene su cuenta y su propio botón de Usar o Equipar), escríbela en el código de la interfaz.
Ruta con código propio: una cuadrícula de objetos para usar y equipar
Esta versión guarda todo el inventario en una sola variable de Lista / tabla llamada inventory, con una cuenta por objeto, y se encarga de usar y equipar desde el código de la interfaz. También usa la variable de Número hp (de 0 a 100, empezando en 80) y la de Texto equipped_weapon.
inventory empieza así:
[{"name":"Potion","icon":"🧪","count":2},{"name":"Iron Sword","icon":"⚔️","count":1}]Sus Reglas de comportamiento:
Los objetos del jugador. Cada uno es {"name", "icon", "count"}. Cuando el jugador encuentre algo, haz push de un objeto nuevo. Deja las cuentas a los botones del inventario.Paneles → Código de frontend → abre index.tsx y sustituye el return <Chat /> que viene por defecto:
export default function MyWorld() {
const api = useYumina();
const msgs = api.messages || [];
const hp = Number(api.variables.hp ?? 80);
const equippedWeapon = String(api.variables.equipped_weapon || "");
const inventory = Array.isArray(api.variables.inventory) ? api.variables.inventory : [];
// Use one of an item: count down, remove at zero, apply its effect
function useItem(itemName) {
const inv = Array.isArray(api.variables.inventory) ? api.variables.inventory : [];
const idx = inv.findIndex((it) => it && it.name === itemName);
if (idx === -1) {
api.showToast("No " + itemName + " left!", "error");
return;
}
const item = inv[idx];
const next = inv.slice();
if (Number(item.count) <= 1) next.splice(idx, 1);
else next[idx] = { ...item, count: Number(item.count) - 1 };
api.setVariable("inventory", next);
if (itemName === "Potion") {
api.setVariable("hp", Math.min(Number(api.variables.hp ?? 0) + 20, 100));
api.showToast("Used a potion! HP +20", "success");
api.injectContext("The player just drank a healing potion.");
}
}
function equipItem(itemName) {
if (equippedWeapon === itemName) {
api.showToast(itemName + " is already equipped!", "info");
return;
}
api.setVariable("equipped_weapon", itemName);
api.showToast("Equipped " + itemName + "!", "success");
api.injectContext("The player just equipped the " + itemName + ".");
}
// Which button each item gets
const itemActions = {
"Potion": { type: "consumable", label: "Use", run: () => useItem("Potion") },
"Iron Sword": { type: "equipment", label: "Equip", run: () => equipItem("Iron Sword") },
};
return (
<Chat renderBubble={(msg) => {
const isLastMsg = msg.messageIndex === msgs.length - 1;
return (
<div>
<div
style={{ color: "#e2e8f0", lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
/>
{isLastMsg && (
<div style={{
marginTop: "16px",
padding: "16px",
background: "rgba(15, 23, 42, 0.6)",
borderRadius: "12px",
border: "1px solid #334155",
}}>
{/* HP bar */}
<div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" }}>
<span style={{ fontSize: "16px" }}>❤️</span>
<div style={{ flex: 1 }}>
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: "4px" }}>
<span style={{ color: "#94a3b8", fontSize: "12px" }}>HP</span>
<span style={{ color: "#e2e8f0", fontSize: "12px", fontWeight: "bold" }}>{hp} / 100</span>
</div>
<div style={{ height: "8px", background: "#1e293b", borderRadius: "4px", overflow: "hidden" }}>
<div style={{
height: "100%",
width: Math.min(hp, 100) + "%",
background: hp > 50
? "linear-gradient(90deg, #22c55e, #4ade80)"
: hp > 20
? "linear-gradient(90deg, #eab308, #facc15)"
: "linear-gradient(90deg, #ef4444, #f87171)",
borderRadius: "4px",
transition: "width 0.3s ease",
}} />
</div>
</div>
</div>
{/* Weapon slot */}
<div style={{
display: "flex", alignItems: "center", gap: "8px", marginBottom: "14px",
padding: "10px 14px", background: "rgba(30, 41, 59, 0.8)",
borderRadius: "8px", border: "1px solid #475569",
}}>
<span style={{ fontSize: "16px" }}>⚔️</span>
<span style={{ color: "#94a3b8", fontSize: "13px" }}>Weapon:</span>
<span style={{
color: equippedWeapon ? "#e2e8f0" : "#475569",
fontSize: "13px",
fontWeight: equippedWeapon ? "600" : "normal",
fontStyle: equippedWeapon ? "normal" : "italic",
}}>
{equippedWeapon || "None"}
</span>
</div>
{/* Item grid */}
{inventory.length === 0 ? (
<div style={{
padding: "24px", textAlign: "center", color: "#475569", fontSize: "13px",
background: "rgba(30, 41, 59, 0.4)", borderRadius: "8px", border: "1px dashed #334155",
}}>
Inventory is empty
</div>
) : (
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))", gap: "8px" }}>
{inventory.map((item, idx) => {
const name = String(item?.name || item);
const action = itemActions[name];
const isEquipped = equippedWeapon === name;
return (
<div key={idx} style={{
display: "flex", flexDirection: "column", alignItems: "center", gap: "6px",
padding: "12px 8px 8px", background: "rgba(30, 41, 59, 0.8)", borderRadius: "8px",
border: isEquipped ? "1px solid #22d3ee" : "1px solid #475569",
}}>
<span style={{ fontSize: "28px" }}>{String(item?.icon || "📦")}</span>
<span style={{ color: "#e2e8f0", fontSize: "12px", fontWeight: "600", textAlign: "center" }}>{name}</span>
<span style={{ color: "#64748b", fontSize: "11px" }}>x{Number(item?.count ?? 1)}</span>
{action && (
<button
onClick={action.run}
style={{
marginTop: "4px", padding: "4px 14px", width: "100%",
borderRadius: "6px", fontSize: "12px", fontWeight: "600", cursor: "pointer",
background: action.type === "consumable" ? "#047857" : isEquipped ? "#4b5563" : "#1e40af",
border: "1px solid " + (action.type === "consumable" ? "#10b981" : isEquipped ? "#6b7280" : "#3b82f6"),
color: action.type === "consumable" ? "#a7f3d0" : isEquipped ? "#9ca3af" : "#bfdbfe",
}}
>
{isEquipped ? "Equipped" : action.label}
</button>
)}
</div>
);
})}
</div>
)}
</div>
)}
</div>
);
}} />
);
}Qué hace el código:
<Chat renderBubble={...} />conserva la lista de mensajes, el cuadro de texto y el desplazamiento de la plataforma; tú dibujas cada burbuja, y el panel va debajo de la últimaapi.variables.inventory,.hpy.equipped_weaponleen las variables por su nombreuseItembusca el objeto por su nombre, le resta uno a la cuenta (o lo quita al llegar a cero) y vuelve a escribir la lista entera conapi.setVariable("inventory", next). Una poción, además, subehpequipItemponeequipped_weapony se lo dice al jugadorapi.injectContext(text)le pasa a la IA una frase para su siguiente respuesta, como el Decirle a la IA de un comportamientoapi.showToast(text, type)muestra un aviso; el tipo es"success","error"o"info"itemActionsdecide qué objetos llevan botón. Añade una línea por cada objeto nuevo que se pueda usar
El estado que aparece junto al archivo en Paneles → Código de frontend debería ser un OK verde.
¿No quieres escribir el código tú?
Pulsa Asistente IA arriba a la derecha del lienzo y descríbelo, por ejemplo «una cuadrícula de objetos con una barra de HP, una ranura de arma y botones de Usar/Equipar».
Cómo puede cambiar la lista la IA
La IA escribe los cambios de la lista como directivas al final de su respuesta; el motor le enseña el formato. Añadir funciona bien:
You search the goblin and find a small vial. [inventory: push {"name":"Potion","icon":"🧪","count":1}]La IA también puede quitar por posición ([inventory: delete 0] quita el primer objeto), pero no puede buscar «la poción» por su nombre ni cambiar de forma fiable la cuenta de un objeto. Deja las cuentas a los botones y dile a la IA en las Reglas de comportamiento que solo añada objetos.
Referencia rápida
| Lo que quieres | Cómo hacerlo |
|---|---|
| Una mochila que la IA va llenando durante la historia | Interfaz → plantilla Inventario |
| Objetos iniciales | El Valor inicial de la variable Inventario |
| Cuentas exactas (pociones, flechas) | Una variable de Número por objeto |
| Usar un objeto con una comprobación | Un comportamiento: El jugador pulsa un botón, SOLO SI la cuenta es mayor que 0, Si no se cumplen las condiciones, decir … |
| Un botón que lo ejecute | Paso de botón Activar un comportamiento |
| Equipar algo | Paso de botón Cambiar una variable sobre una variable de Texto |
| Ocultar un botón cuando ya se ha hecho | Cuándo mostrar → Cuando una variable cumple |
| Contárselo a la IA en el momento | Decirle a la IA en un comportamiento, o api.injectContext() en código |
| Una cuadrícula con botones y cuentas por objeto | Código de interfaz (la ruta con código propio de arriba) |
Esta es la receta n.º 7
El inventario combina con la tienda (entra oro, salen objetos) y con el rastreador de misiones (objetos como recompensa). El mismo reparto sirve para otras listas: deja que la IA lleve la lista de la historia, y dale su propia variable a todo lo que necesite números exactos.
