API
Bloques, ítems y recetas
El contenido se declara como una lista de propiedades. Crafting crea los objetos reales del juego en el momento adecuado y genera los modelos, tablas de botín, etiquetas, recetas y traducciones que necesitan. Tú no escribes ningún archivo de datos del juego.
Ítems
item "ruby" { rarity: "rare" }
item "ruby_apple" {
food: { nutrition: 8, saturation: 1.2, always_edible: true }
glint: true
}
item "ruby_pickaxe" { tool: { type: "pickaxe", tier: "diamond", speed: 9 }, tab: "tools_and_utilities" }| Propiedad | Valores | Por defecto |
|---|---|---|
stack o max_stack |
1–99 | 64 |
rarity |
common, uncommon, rare, epic (color del nombre) |
common |
glint |
brillo encantado | false |
durability |
número; obliga a stack: 1 |
0 |
fireproof |
no se quema en lava ni fuego | false |
food |
número, o { nutrition, saturation, always_edible } |
— |
tool |
"pickaxe", o { type, tier, damage, speed }; type: pickaxe, axe, shovel, hoe, sword |
— |
tab |
pestaña del inventario creativo (abajo) | ingredients |
texture |
ruta de la textura | item/<id> |
on_use |
fun(player, stack); devuelve true si gestiona el uso |
— |
armor |
{ slot, defense, toughness, knockback_resistance, texture } |
— |
on_hit |
fun(attacker, target, stack) |
— |
cooldown |
segundos | 0 |
Armadura, armas y enfriamientos
Un objeto con armor se puede vestir. slot es head, chest, legs o feet. defense, toughness y knockback_resistance (0 a 1) son los valores de armadura del juego: un peto de hierro tiene 6 de defensa y el diamante 2 de dureza.
item "frost_helmet" { texture: "minecraft:item/iron_helmet", armor: { slot: "head", defense: 3, texture: "frost" } }
item "frost_chestplate" { armor: { slot: "chest", defense: 8, toughness: 2 }, durability: 400 }texture es cómo se ve puesta. frost significa textures/entity/equipment/humanoid/frost.png en el mod y, para las piernas, textures/entity/equipment/humanoid_leggings/frost.png, con la misma disposición que las texturas de armadura del juego. Sin ella, la armadura se ve como la de hierro.
on_hit se ejecuta cuando el objeto se usa para golpear, ya sea un jugador o un mob.
item "frost_blade" {
texture: "minecraft:item/diamond_sword"
tool: "sword"
cooldown: 3
on_hit: fun(attacker, target, stack) {
target.add_effect("minecraft:slowness", 4, 4)
target.world.spawn_particle("minecraft:snowflake", target.location.add(0, 1, 0), 20, 0.4, 0.02)
}
}cooldown (en segundos) bloquea el objeto ese tiempo tras cada uso que su on_use aceptó, y la barra de acceso rápido lo muestra. Desde código: player.set_cooldown("minecraft:feather", 2) y player.has_cooldown(id). En Kotlin: armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") para armaduras y onHit = { ctx -> … } para armas (ctx.attacker, ctx.target, ctx.stack).
item("frost_helmet") { armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") }Bloques
block "ruby_ore" {
hardness: 3
tool: "pickaxe"
tier: "iron"
loot: [
{ item: "rubies:ruby", count: [1, 2] },
{ item: "minecraft:diamond", chance: 0.1 }
]
}
block "ruby_block" { hardness: 5, light: 7, tool: "pickaxe", sound: "metal" }
block "ruby_lamp" { hardness: 0.3, light: 15, sound: "glass", transparent: true, tab: "functional_blocks" }
block "ruby_bricks" { hardness: 2, tool: "pickaxe", drops: "ruby", drop_count: [2, 4] }| Propiedad | Valores | Por defecto |
|---|---|---|
hardness |
número; -1 = irrompible |
1.5 |
resistance |
resistencia a explosiones | = hardness |
light |
0–15 | 0 |
light_when |
un estado de interruptor, como "lit": el bloque brilla solo mientras está encendido (ver Estados de bloque) |
siempre |
sound |
stone, wood, gravel, grass, metal, glass, wool, sand | stone |
like |
un bloque con el que se hace igual, del juego ("minecraft:end_stone") o tuyo: su dureza, resistencia, sonido y si necesita la herramienta adecuada para soltar algo son los de ese bloque |
— |
kind |
uno de los tipos del juego: escaleras, puerta, cofre, cultivo… (ver Tipos de bloque) | cube |
tool, tier |
herramienta necesaria para que suelte algo, y material mínimo de esa herramienta: wood, stone, iron, diamond, netherite | —, wood |
drops, drop_count |
un solo objeto: id; número o [mín, máx] |
el propio bloque, 1 |
loot |
varios objetos: [{ item, count, chance, tool, fortune }, …]. Cada uno se sortea por separado; count es un número o [mín, máx] y chance va de 0 a 1 (por defecto 1). tool y fortune: ver Lo que suelta más abajo |
— |
friction |
0.6 normal, 0.98 hielo | 0.6 |
transparent |
deja ver a través (cristal, hojas) | false |
has_item |
registrar también el ítem del bloque; sin él, el bloque no suelta nada salvo que drops o loot digan qué |
true |
tab |
pestaña del inventario creativo | building_blocks |
on_use |
fun(player, block); devuelve true si gestiona el uso. Un jugador que va agachado con algo en la mano lo coloca en su lugar, como con los bloques del juego |
— |
Pestañas: building_blocks, colored_blocks, natural_blocks, functional_blocks, redstone_blocks, tools_and_utilities, combat, food_and_drinks, ingredients, spawn_eggs. En Kotlin están en CreativeTabs.
Para que una criatura suelte algo más al morir, añádelo a e.drops en el evento entity_death (ver Eventos).
Los bloques que no son cubos, los bloques de los tipos del juego, las criaturas, las menas, los sonidos y las partículas se declaran igual y tienen su propia página: Formas de bloque, Tipos de bloque, Criaturas, Generación de mundo, Sonidos y partículas.
Lo que suelta: herramientas y fortuna
Un objeto de loot puede necesitar una herramienta, y la fortuna puede dar más de él:
block "ruby_ore" {
kind: "ore"
tool: "pickaxe"
tier: "iron"
xp: [1, 4]
loot: [
{ item: "ruby", count: [1, 2], fortune: true },
{ item: "ruby_ore", tool: "silk_touch" }
]
}
block "frost_leaves" {
kind: "leaves"
loot: [
{ item: "frost_leaves", tool: "shears" },
{ item: "frost_sapling", chance: 0.05, fortune: true }
]
}tool: "silk_touch": solo se suelta cuando el bloque se rompe con toque de seda.tool: "shears": solo con tijeras o con toque de seda, como las hojas y enredaderas del juego se sueltan a sí mismas.- Cuando algunos objetos necesitan una herramienta, los que no la necesitan se sueltan solo cuando esa herramienta no se usa: la mena de arriba se suelta a sí misma con toque de seda y sus rubíes en caso contrario.
fortune: true: la fortuna da más de él, más de sucountcomo con las menas del juego, o una mejorchancecomo con los brotes que sueltan las hojas.
En Kotlin: drop("ruby", 1..2, tool = null, fortune = true) y drop("ruby_ore", tool = DropTool.SILK_TOUCH).
block("ruby_ore") {
kind = BlockKind.ORE
xp = 1..4
drop("ruby", 1..2, tool = null, fortune = true)
drop("ruby_ore", tool = DropTool.SILK_TOUCH)
}Recetas
recipe shaped "ruby_block" {
pattern: ["RRR", "RRR", "RRR"]
keys: { R: "ruby" }
result: "ruby_block"
}
recipe shapeless "rubies_from_block" { ingredients: ["ruby_block"], result: "ruby", count: 9 }
recipe smelting "ruby_from_ore" { input: "ruby_ore", result: "ruby", xp: 1.0 }Comportamiento al usar
on_use es la única lógica que vive junto a los datos. Si crece más de un par de líneas, escribe la función en lib/ e impórtala:
import { lift } from "/lib/teleport"
item "lift_wand" { stack: 1, rarity: "uncommon", on_use: lift }fun lift(player, stack) {
player.teleport(player.location.add(0, 10, 0))
return true
}Bloques que reaccionan
Un bloque puede ejecutar código cuando le pasa algo a él o a su alrededor. Cada manejador recibe el bloque y, cuando corresponde, la entidad.
| Propiedad | Se ejecuta cuando |
|---|---|
on_step: fun(block, entity) |
una entidad camina sobre el bloque. |
on_inside: fun(block, entity) |
una entidad está dentro del bloque. El bloque debe dejar pasar a las entidades: un portal, una planta o una forma no sólida. |
on_random_tick: fun(block) |
de vez en cuando, al azar, como el crecimiento de los cultivos: cada 68 segundos aproximadamente por bloque. |
on_tick: fun(block) |
pasó el tiempo pedido con block.schedule(ticks) (20 ticks = 1 segundo). |
on_neighbor_change: fun(block) |
cambió un bloque contiguo: lo colocaron, lo rompieron o recibió señal. |
on_placed: fun(block, entity) |
alguien colocó el bloque; entity es quien. |
block "jump_pad" { on_step: fun(b, e) { e.velocity = vector(0, 1.1, 0) } }
block "blinker" {
light: 7
on_tick: fun(b) {
b.world.spawn_particle("minecraft:end_rod", b.location.add(0.5, 1.2, 0.5), 4, 0.1, 0.01)
b.schedule(20)
}
on_neighbor_change: fun(b) { b.schedule(1) }
}En Kotlin los manejadores son onStep, onInside, onRandomTick, onTick, onNeighborChange y onPlaced. Cada uno recibe un BlockEventContext con block y, si existe, entity:
block("pad") { onStep = { ctx -> ctx.entity?.velocity = Vector(0.0, 1.1, 0.0) } }Portales
Un bloque con portal lleva a quien esté dentro a otra dimensión. Indica la dimensión de destino, o to y back para elegir adónde lleva desde allí:
block "sky_gate" { portal: "gatemod:sky", light: 11 }
block "sky_gate_two_way" { portal: { to: "gatemod:sky", back: "minecraft:overworld" } }La entidad llega al suelo en las mismas x y z y allí encuentra un bloque de portal para volver; si no hay ninguno cerca, se coloca uno junto a la llegada. Las entidades esperan un momento antes de volver a usar un portal. Los bloques de portal dejan pasar a las entidades. La dimensión se declara con dimension (ver @dim@). En Kotlin:
block("sky_gate") { portal("gatemod:sky") }Estados de bloque
states da al bloque valores que recuerda y puede mostrar:
block "lamp" {
model: "block/lamp"
states: ["facing", "lit"]
on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
on_neighbor_change: fun(b) { b.set_state("lit", b.redstone_power > 0) }
}
block "crop" { states: [{ stage: 3 }, { wet: true }] }"facing": norte, este, sur u oeste. Se orienta hacia el jugador que coloca el bloque, y el modelo gira con él."lit": un nombre solo es un interruptor encendido/apagado, apagado por defecto.{ lit: true }lo deja encendido por defecto.{ stage: 3 }: un número de 0 al máximo que indiques (de 1 a 15)."facing_all": como"facing", pero también hacia arriba y hacia abajo. Se orienta hacia el jugador que lo coloca, y el modelo se inclina para arriba y para abajo.{ shape: ["top", "middle", "bottom"] }: uno de unos pocos nombres, el primero por defecto. Los nombres son letras minúsculas, dígitos y_.
Se leen y cambian con block.state("lit") y block.set_state("lit", true). También funcionan con los bloques del juego, por ejemplo el facing y el lit de un horno.
Un estado distinto del valor por defecto puede verse diferente. Añade una textura en textures/block/<id>_<nombre>.png (un interruptor encendido) o <id>_<nombre><n>.png (un número) o <id>_<valor>.png (un nombre), o un modelo en models/block/<id>_<nombre>.json. Primero se prueba la combinación completa (<id>_lit_stage2) y luego cada estado por separado. En el ejemplo, lamp.json y lamp_lit.json son los dos modelos del mod, por ejemplo con el padre minecraft:block/orientable y texturas de frente, lado y arriba.
Luz según el estado. light_when: "lit" hace que el light del bloque brille solo mientras ese interruptor está encendido, como en una lámpara:
block "lamp" {
light: 15
light_when: "lit"
states: ["lit"]
on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
}En Kotlin: facing(), facingAll(), toggle("lit"), number("stage", 3), choice("shape", "top", "middle", "bottom"), y block.state(…) y block.setState(…) en el código:
block("lamp") {
model = "block/lamp"
facing()
toggle("lit")
number("stage", 3)
}Efectos
Un mod puede añadir sus propios efectos de estado. Aparecen en la pantalla del jugador como los del juego y se dan y se leen de la misma manera.
effect "frozen" {
color: "#88CCFF"
kind: "harmful"
on_tick: fun(e, level) { e.damage(level) }
}color: el color de sus partículas y del fondo de su icono.kind:beneficial,harmfuloneutral.on_tick:fun(e, level), se ejecuta cada segundo mientras una entidad tiene el efecto, con su nivel (1 es I).- El icono es
textures/mob_effect/<id>.pngdel mod.
Se da con e.add_effect("mymod:frozen", 3, 1) (id, segundos, nivel) y se comprueba con e.has_effect("mymod:frozen"), como los efectos del juego (ver El mundo y los jugadores). En Kotlin:
content.effect("frozen") {
color = 0x88CCFF
kind = EffectKind.HARMFUL
onTick = { e, level -> /* … */ }
}Redstone
block "battery" {
texture: "minecraft:block/copper_block"
redstone: true
on_use: fun(p, b) { if b.state("power") > 0 { b.set_state("power", 0) } else { b.set_state("power", 15) }; return true }
}redstone: true hace que el bloque emita una señal tan fuerte como su estado power, un número de 0 a 15 que se añade automáticamente. b.set_state("power", 15) lo enciende, y los cables, lámparas y puertas de alrededor lo siguen. block.redstone_power es la potencia que el bloque recibe de su alrededor, y on_neighbor_change se ejecuta cuando cambia (la lámpara de arriba lo usa).
En Kotlin: redstone = true y block.redstonePower.
block("battery") {
texture = "minecraft:block/copper_block"
redstone = true
// in a handler: ctx.block.redstonePower
}Inventarios
block "crate" { texture: "minecraft:block/barrel_side", inventory: 27 }inventory: 27 da al bloque ese número de casillas (de 9 a 54, en filas de 9). Conserva sus objetos al reiniciar, al usarlo se abre una pantalla de cofre (salvo que su on_use se haya quedado con el clic) y al romperlo suelta lo que contiene. Con 1 a 8 casillas no hay pantalla: es el propio mod quien mete y saca los objetos.
Desde el código, block.inventory (también funciona con los cofres y barriles del juego) ofrece set(slot, item(…)), get(slot), count(id) y add(…). En Kotlin: inventory = 27.
block("crate") {
texture = "minecraft:block/barrel_side"
inventory = 27
}Objetos mostrados sobre un bloque. shows_item: 14 hace que el objeto de la primera casilla del bloque flote sobre él, girando, a tantos píxeles sobre su base. Todos los jugadores lo ven, y desaparece cuando la casilla se vacía o el bloque se rompe. Un pedestal toma el objeto de la mano del jugador y se lo devuelve:
block "pedestal" {
shape: [[2, 0, 2, 14, 12, 14]]
inventory: 1
shows_item: 14
on_use: fun(p, b) {
let inv = b.inventory
if inv.get(0) == null and p.held_item != null {
inv.set(0, p.held_item)
p.set_held_item(null)
} else if inv.get(0) != null {
p.give(inv.get(0))
inv.set(0, null)
}
return true
}
}player.set_held_item(stack) pone un objeto en la mano principal del jugador, o la vacía con null. En Kotlin: showsItem = 14 y player.setHeldItem(stack).
block("pedestal") {
box(2, 0, 2, 14, 12, 14)
inventory = 1
showsItem = 14
onUse { ctx ->
val inv = ctx.block.inventory!!
val held = ctx.player.heldItem
val shown = inv[0]
if (shown == null && held != null) { inv[0] = held; ctx.player.setHeldItem(null) }
else if (shown != null) { ctx.player.give(shown); inv[0] = null }
true
}
}Texturas
Las texturas viajan dentro del mod; nadie tiene que instalar un paquete de recursos. El nombre del archivo es el id:
| Mod | Dónde |
|---|---|
| CraftScript (carpeta) | mods/rubies/textures/item/ruby.png, mods/rubies/textures/block/ruby_ore.png |
| Kotlin o Java | src/main/resources/assets/rubies/textures/item/ruby.png |
Si un mod trae sus propios modelos, recetas o tablas de botín en esas mismas carpetas, Crafting los respeta.
En Kotlin
package com.example.rubies.content
object Blocks {
fun register(content: ContentBuilder) {
content.block("ruby_ore") {
hardness = 3f
requiresTool = ToolType.PICKAXE
minTier = ToolTier.IRON
drop("ruby", 1..2)
drop("minecraft:diamond", chance = 0.1)
}
content.block("ruby_block") { hardness = 5f; luminance = 7; sound = SoundGroup.METAL }
}
}Ids. Sin namespace, un id es de tu mod ("ruby" es rubies:ruby). Para lo del juego escribe el namespace: "minecraft:diamond". No se puede registrar contenido en el namespace minecraft.