API

方块、物品与配方

内容以一组属性的形式声明。Crafting 会在合适的时机创建真正的游戏对象,并生成它们所需的模型、战利品表、标签、配方和翻译。你不需要编写任何游戏数据文件。

物品

content/items.craftCraftScript
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" }
属性 取值 默认值
stack 或 max_stack 1–99 64
rarity common、uncommon、rare、epic(名称颜色) common
glint 附魔光效 false
durability 数字;会强制 stack: 1 0
fireproof 不会在熔岩或火中烧毁 false
food 数字,或 { nutrition, saturation, always_edible } —
tool "pickaxe",或 { type, tier, damage, speed };type:pickaxe、axe、shovel、hoe、sword —
tab 创造模式物品栏标签页(见下文) ingredients
texture 纹理路径 item/<id>
on_use fun(player, stack);如果它处理了使用动作,返回 true —
armor { slot, defense, toughness, knockback_resistance, texture } —
on_hit fun(attacker, target, stack) —
cooldown 秒 0

护甲、武器与冷却

带有 armor 的物品可以穿戴。slot 是 head、chest、legs 或 feet。defense、toughness 和 knockback_resistance(0 到 1)是游戏的护甲数值:铁胸甲有 6 点防御,钻石有 2 点韧性。

content/armor.craftCraftScript
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 是穿在身上的外观。frost 指模组中的 textures/entity/equipment/humanoid/frost.png,腿部则是 textures/entity/equipment/humanoid_leggings/frost.png,布局与游戏的护甲纹理相同。不设置时,护甲看起来像铁甲。

无论是玩家还是生物,用该物品击打时都会运行 on_hit。

content/frost_blade.craftCraftScript
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(秒)会在其 on_use 处理的每次使用之后,让物品在这段时间内无法使用,快捷栏会显示冷却。在代码中:player.set_cooldown("minecraft:feather", 2) 和 player.has_cooldown(id)。在 Kotlin 中,护甲用 armor(EquipmentSlot.HEAD, defense = 3, texture = "frost"),武器用 onHit = { ctx -> … }(ctx.attacker、ctx.target、ctx.stack)。

content/Armor.ktKotlin
item("frost_helmet") { armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") }

方块

content/ores.craftCraftScript
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] }
属性 取值 默认值
hardness 数字;-1 = 无法破坏 1.5
resistance 爆炸抗性 = hardness
light 0–15 0
light_when 一个开关状态,如 "lit":方块只在它开启时发光(见方块状态) 始终
sound stone、wood、gravel、grass、metal、glass、wool、sand stone
like 它参照制作的方块,游戏的("minecraft:end_stone")或你自己的:它的硬度、抗性、声音,以及是否必须用对工具才会掉落东西,都与那个方块相同 —
kind 游戏的种类之一:楼梯、门、箱子、作物……(见方块种类) cube
tool、tier 方块掉落任何东西所需的工具,以及该工具的最低材质:wood、stone、iron、diamond、netherite —、wood
drops、drop_count 单个物品:id;数字或 [min, max] 方块本身、1
loot 多个物品:[{ item, count, chance, tool, fortune }, …]。每一项单独掷骰;count 是数字或 [min, max],chance 取值 0 到 1(默认为 1)。tool 和 fortune:见下面的掉落物 —
friction 0.6 为普通,0.98 为冰 0.6
transparent 可以透过它看到后面(玻璃、树叶) false
has_item 同时注册该方块的物品;没有物品时,除非 drops 或 loot 指明,否则方块不掉落任何东西 true
tab 创造模式物品栏标签页 building_blocks
on_use fun(player, block);如果它处理了使用动作,返回 true。潜行并手持物品的玩家会像对游戏方块那样改为放置该物品 —

标签页:building_blocks、colored_blocks、natural_blocks、functional_blocks、redstone_blocks、tools_and_utilities、combat、food_and_drinks、ingredients、spawn_eggs。在 Kotlin 中它们位于 CreativeTabs。

要让生物死亡时额外掉落东西,在 entity_death 事件中把它加入 e.drops(见事件)。

非立方体方块、游戏种类的方块、生物、矿石、声音和粒子的声明方式相同,各有专门的页面:方块形状、方块种类、生物、世界生成、声音与粒子。

掉落物:工具与时运

loot 中的掉落物可以要求使用某种工具,时运也可以让它掉得更多:

content/drops.craftCraftScript
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":只有用精准采集破坏方块时才会掉落。tool: "shears":只有用剪刀或精准采集时才会掉落,就像游戏的树叶和藤蔓掉落自身那样。
  • 当某些掉落物需要工具时,不需要工具的掉落物只在没有使用那种工具时才会出现:上面的矿石在精准采集下掉落自身,其他情况下掉落红宝石。
  • fortune: true:时运会让它掉得更多,像游戏的矿石那样增加 count,或像树叶掉落树苗那样提高 chance。

在 Kotlin 中:drop("ruby", 1..2, tool = null, fortune = true) 和 drop("ruby_ore", tool = DropTool.SILK_TOUCH)。

content/Drops.ktKotlin
block("ruby_ore") {
    kind = BlockKind.ORE
    xp = 1..4
    drop("ruby", 1..2, tool = null, fortune = true)
    drop("ruby_ore", tool = DropTool.SILK_TOUCH)
}

配方

content/recipes.craftCraftScript
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 }

使用行为

on_use 是唯一与数据放在一起的逻辑。如果它超过几行,就把函数写在 lib/ 中再导入:

content/wands.craftCraftScript
import { lift } from "/lib/teleport"

item "lift_wand" { stack: 1, rarity: "uncommon", on_use: lift }
lib/teleport.craftCraftScript
fun lift(player, stack) {
    player.teleport(player.location.add(0, 10, 0))
    return true
}

会做出反应的方块

方块可以在自身或周围发生某些事情时运行代码。每个处理函数都会收到方块,以及(适用时)实体。

属性 运行时机
on_step: fun(block, entity) 实体走到方块上时。
on_inside: fun(block, entity) 实体处于方块内部时。方块必须能让实体通过:传送门、植物或非实心形状。
on_random_tick: fun(block) 不时随机触发,就像作物生长:每个方块大约每 68 秒一次。
on_tick: fun(block) 通过 block.schedule(ticks) 请求的时间已到(20 刻 = 1 秒)。
on_neighbor_change: fun(block) 相邻方块发生了变化:被放置、被破坏或被通电。
on_placed: fun(block, entity) 有人放置了该方块;entity 是放置者。
content/reactions.craftCraftScript
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) }
}

在 Kotlin 中,处理函数是 onStep、onInside、onRandomTick、onTick、onNeighborChange 和 onPlaced。每个都会收到一个 BlockEventContext,包含 block,以及存在时的 entity:

content/Reactions.ktKotlin
block("pad") { onStep = { ctx -> ctx.entity?.velocity = Vector(0.0, 1.1, 0.0) } }

传送门

带有 portal 的方块会把身处其中的对象送到另一个维度。指定要前往的维度,或用 to 和 back 选择从那里通向何处:

content/portals.craftCraftScript
block "sky_gate" { portal: "gatemod:sky", light: 11 }

block "sky_gate_two_way" { portal: { to: "gatemod:sky", back: "minecraft:overworld" } }

实体会在相同 x 和 z 的地面上抵达,并在那里找到一个传送门方块用来返回;如果附近没有,就会在抵达点旁边放置一个。实体再次使用传送门前会稍等片刻。传送门方块可让实体通过。维度本身用 dimension 声明(见@dim@)。在 Kotlin 中:

content/Portals.ktKotlin
block("sky_gate") { portal("gatemod:sky") }

方块状态

states 为方块提供它会记住并可展示的值:

content/states.craftCraftScript
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":北、东、南或西。它会朝向放置它的玩家,模型也随之转动。
  • "lit":单独一个名字就是开/关开关,默认关闭。{ lit: true } 让它默认开启。
  • { stage: 3 }:从 0 到你给定的最大值(1 到 15)的数字。
  • "facing_all":与 "facing" 类似,但也可以朝上和朝下。它会朝向放置它的玩家,朝上或朝下时模型会翻转。
  • { shape: ["top", "middle", "bottom"] }:几个名称中的一个,默认是第一个。名称由小写字母、数字和 _ 组成。

用 block.state("lit") 读取,用 block.set_state("lit", true) 修改。游戏自带的方块也适用,例如熔炉的 facing 和 lit。

偏离默认值的状态可以有不同的外观。把纹理放在 textures/block/<id>_<名称>.png(已开启的开关)或 <id>_<名称><n>.png(数字)或 <id>_<值>.png(名称),或把模型放在 models/block/<id>_<名称>.json。会先尝试完整组合(<id>_lit_stage2),再逐个尝试单个状态。示例中的 lamp.json 和 lamp_lit.json 是模组的两个模型,例如以 minecraft:block/orientable 为父级,并使用正面、侧面和顶面纹理。

按状态发光。light_when: "lit" 让方块的 light 只在该开关开启时发光,就像灯一样:

content/lamp.craftCraftScript
block "lamp" {
    light: 15
    light_when: "lit"
    states: ["lit"]
    on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
}

在 Kotlin 中:facing()、facingAll()、toggle("lit")、number("stage", 3)、choice("shape", "top", "middle", "bottom"),代码中用 block.state(…) 和 block.setState(…):

content/States.ktKotlin
block("lamp") {
    model = "block/lamp"
    facing()
    toggle("lit")
    number("stage", 3)
}

效果

模组可以添加自己的状态效果。它们会像游戏自带的效果一样显示在玩家的屏幕上,并以同样的方式施加和读取。

content/frozen.craftCraftScript
effect "frozen" {
    color: "#88CCFF"
    kind: "harmful"
    on_tick: fun(e, level) { e.damage(level) }
}
  • color:粒子颜色和图标背景颜色。
  • kind:beneficial、harmful 或 neutral。
  • on_tick:fun(e, level),实体拥有该效果期间每秒运行一次,并接收其等级(1 即 I)。
  • 图标是模组的 textures/mob_effect/<id>.png。

用 e.add_effect("mymod:frozen", 3, 1)(id、秒数、等级)施加,用 e.has_effect("mymod:frozen") 检查,与游戏自带的效果相同(见世界与玩家)。在 Kotlin 中:

content/Frozen.ktKotlin
content.effect("frozen") {
    color = 0x88CCFF
    kind = EffectKind.HARMFUL
    onTick = { e, level -> /* … */ }
}

红石

content/battery.craftCraftScript
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 让方块发出与其 power 状态(自动添加的 0 到 15 的数字)一样强的信号。b.set_state("power", 15) 将其开启,周围的导线、灯和门随之响应。block.redstone_power 是方块从周围接收到的强度,它变化时会运行 on_neighbor_change(上面的灯就是这样做的)。

在 Kotlin 中:redstone = true 和 block.redstonePower。

content/Battery.ktKotlin
block("battery") {
    texture = "minecraft:block/copper_block"
    redstone = true
    // in a handler: ctx.block.redstonePower
}

物品栏

content/crate.craftCraftScript
block "crate" { texture: "minecraft:block/barrel_side", inventory: 27 }

inventory: 27 为方块提供相应数量的格子(9 到 54,每行 9 格)。物品在重启后保留;使用它会打开箱子界面(除非它的 on_use 接管了点击);破坏它会掉落其中的物品。1 到 8 个格子时没有界面:由模组自己放入和取出物品。

在代码中,block.inventory(对游戏自带的箱子和木桶同样有效)提供 set(slot, item(…))、get(slot)、count(id) 和 add(…)。在 Kotlin 中:inventory = 27。

content/Crate.ktKotlin
block("crate") {
    texture = "minecraft:block/barrel_side"
    inventory = 27
}

显示在方块上的物品。shows_item: 14 会让方块第一个格子里的物品悬浮在方块上方、高出其底部那么多像素的位置并不停旋转。所有玩家都能看到它,格子清空或方块被破坏时它就消失。基座会接收玩家手中的物品,也会把它还回去:

content/pedestal.craftCraftScript
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) 把物品放进玩家的主手,传入 null 则清空。在 Kotlin 中:showsItem = 14 和 player.setHeldItem(stack)。

content/Pedestal.ktKotlin
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
    }
}

纹理

纹理随模组一起提供;没人需要安装资源包。文件名就是 id:

模组 位置
CraftScript(文件夹) mods/rubies/textures/item/ruby.png、mods/rubies/textures/block/ruby_ore.png
Kotlin 或 Java src/main/resources/assets/rubies/textures/item/ruby.png

如果模组在这些文件夹中自带了模型、配方或战利品表,Crafting 会使用它们。

Kotlin 中

content/Blocks.ktKotlin
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 }
    }
}

id。不带命名空间的 id 属于你的模组("ruby" 即 rubies:ruby)。引用游戏内容时要写命名空间:"minecraft:diamond"。你不能在 minecraft 命名空间中注册内容。