API

世界生成

矿石、树木、花、结构和生物群系会自动出现在世界中。你声明是什么、在哪里,新的区块就会生成它们。

矿石

content/ruby_ore.craftCraftScript
block "ruby_ore" { hardness: 3, tool: "pickaxe", tier: "iron" }

ore "ruby_ore" {
    replaces: "stone_and_deepslate"
    size: 9
    per_chunk: 8
    height: [-60, 60]
}
属性 取值 默认值
block 要放置的方块:"mymod:other" 与矿石同名的方块
replaces stone、deepslate、stone_and_deepslate(主世界)、netherrack(下界)或 end_stone(末地) stone_and_deepslate
size 每个矿脉的方块数,1–64 8
per_chunk 每个区块尝试生成的矿脉数 8
height [min, max],高度范围 [-64, 64]
biomes 只在这些生物群系中,用 id 或标签指定 它所在岩石存在的每个生物群系

没有 biomes 时,矿石会出现在 replaces 所属维度的每个生物群系中;end_stone 矿石则出现在末地的每个生物群系中。方块本身和其他方块一样声明,带有硬度、工具和战利品;见方块、物品与配方。

content/void_ore.craftCraftScript
ore "void_ore" {
    replaces: "end_stone"
    size: 5
    per_chunk: 6
    height: [10, 80]
    biomes: ["mymod:crystal_glade"]
}

树木

树由笔直的树干和一个树叶球组成。先声明两个方块,再声明树。

content/ruby_tree.craftCraftScript
block "ruby_log" { hardness: 2, tool: "axe", sound: "wood" }
block "ruby_leaves" { hardness: 0.2, transparent: true, sound: "grass" }

tree "ruby_tree" {
    trunk: "ruby_log"
    leaves: "ruby_leaves"
    height: 6
    height_variation: 2
    leaves_radius: 2
    per_chunk: 2
    biomes: ["#minecraft:is_forest", "minecraft:plains"]
}
属性 取值 默认值
trunk 树干方块,来自模组或游戏:"ruby_log", "minecraft:oak_log" —
leaves 树叶方块 —
height 树干高度(方块数) 5
height_variation 每棵树高度的变化幅度 2
leaves_radius 树叶球的半径(方块数) 2
per_chunk 每个区块尝试生成的树数 1
biomes 出现的生物群系(id 或标签) —

生物群系可以用 id(minecraft:plains)或标签(#minecraft:is_forest)指定,可任意混用。树干和树叶可以是模组的方块,也可以是游戏的方块。

植物与花丛

植物是一种方块,绘制成两张交叉的图像,就像花:没有东西会与它碰撞,光线可以穿过。它的贴图是 textures/block/ruby_flower.png,背景透明。

content/ruby_flowers.craftCraftScript
block "ruby_flower" { plant: true, hardness: 0 }

patch "ruby_flowers" {
    block: "ruby_flower"
    biomes: ["#minecraft:is_overworld"]
    rarity: 4
    size: 32
    spread: 7
}

花丛会把植物散布到世界中:

属性 取值 默认值
block 植物方块 —
biomes 出现的生物群系(id 或标签) —
rarity 每隔这么多区块出现一处花丛 4
size 每处花丛尝试放置的植物数 32
spread 距花丛中心的最大距离(方块数) 7

植物只会放置在主世界地面(草方块、泥土、苔藓和泥巴)上方的空气中。若想让植物长在其他地面上、墙上或从天花板垂下,请编写一个地物:见用代码生成。

结构

结构是你自己建造的建筑:

  1. 在游戏中建造它。
  2. 用结构方块保存它。
  3. 把 .nbt 文件复制到模组的 structures/ 文件夹:CraftScript 模组放在脚本旁边,Kotlin 或 Java 的 jar 放在 assets/<模组 id>/structures/。

然后声明它:

content/ruby_shrine.craftCraftScript
structure "ruby_shrine" {
    file: "ruby_shrine"
    biomes: ["#minecraft:is_overworld"]
    spacing: 24
    separation: 8
    buried: false
}
属性 取值 默认值
file 不带扩展名的 .nbt 文件名 第一个 variant,或结构的名称
biomes 出现的生物群系(id 或标签) —
spacing 大约每隔这么多区块出现一个结构 24
separation 彼此至少相距这么多区块;小于 spacing 8
buried true 会把它沉入地下,适合做废墟 false
offset_y 比它所立足的地表高出的格数;为负数则下沉 0
adapt_ground true 会填平它下面的地面并清理它周围;false 则原样放置 true
heights [min, max]:不贴着地表,而是无论地表在哪里,都放在这个范围内的随机高度(空中的房子) 在地表上

用 /locate structure <模组 id>:ruby_shrine 查找,用 /place structure <模组 id>:ruby_shrine 放置。

变体、替换与风化

一个结构可以来自多个文件,它的方块也可以在放置时改变。这样做出的遗迹每次都不完全相同:

content/old_ruins.craftCraftScript
structure "old_ruins" {
    variants: [{ file: "ruins_small", weight: 3 }, "ruins_tower"]
    biomes: ["#minecraft:is_forest"]
    spacing: 20
    separation: 6
    offset_y: -2
    skip: ["minecraft:air"]
    replace: [{ block: "minecraft:stone_bricks", with: "minecraft:cracked_stone_bricks", chance: 0.3 }]
    decay: { left: 0.8, blocks: ["minecraft:stone_bricks", "minecraft:cracked_stone_bricks"] }
}
属性 取值 默认值
variants 它可能是其中之一的文件:名称,或 { file, weight };按权重随机选出一个 只有 file,权重 1
skip 它的文件中不放置的方块。"minecraft:air" 会在文件为空气的地方保留世界原有的方块 —
replace [{ block, with, chance }]:它文件中的某个方块以该概率(大于 0,最大为 1)变成另一个方块 chance 1
decay { left, blocks }:blocks(id 或标签)中只保留 left(0–1)的比例;没有 blocks 时,对任何方块都适用 全部保留

由相连的部件组成的结构

村庄或地牢由相连的部件组成:街道通向房子,走廊通向房间。每个部件是一个含有游戏拼图方块的文件,每个拼图方块用 <模组 id>:<池> 的形式写出下一个部件从哪个池中取。声明这些池;结构从第一个池开始。

content/hamlet.craftCraftScript
structure "hamlet" {
    biomes: ["minecraft:plains"]
    spacing: 34
    separation: 8
    depth: 6
    pools: [
        { name: "hamlet/square", pieces: [{ file: "hamlet/square" }] },
        { name: "hamlet/streets", fallback: "hamlet/street_ends",
          pieces: [{ file: "hamlet/street_straight", weight: 3, follows_ground: true },
                   { file: "hamlet/street_corner", follows_ground: true }],
          replace: [{ block: "minecraft:stone_bricks", with: "minecraft:oak_planks", where: "minecraft:water" }] },
        { name: "hamlet/street_ends", pieces: [{ file: "hamlet/street_end", follows_ground: true }] },
        { name: "hamlet/houses", none: 2,
          pieces: [{ file: "hamlet/house_small", weight: 4 }, { file: "hamlet/house_big" }] },
    ]
}
属性 取值 默认值
pools 部件的池;第一个是它的起点 来自 variants 或 file 的一个部件
depth 它从第一个部件起向外延伸多少个部件 1

每个池有:

键 取值 默认值
name 它的拼图方块所使用的名称,如 hamlet/streets —
pieces [{ file, weight, follows_ground }]。follows_ground: true 会让部件顺着其下方的地面弯曲,适用于街道和小路;false 则保持笔直,适用于房子 权重 1,false
features [{ id, weight }]:作为部件的地物,你的或游戏的(minecraft:chorus_plant) —
none 完全不放部件的权重,用来留出空地 0
fallback 在没有合适部件的地方(如街道的尽头)使用的池 无
skip、replace 与单个结构相同。在池中,替换可以加上 where:只在世界原本就有那种方块的地方(水上街道的砖变成木板) —

所有部件都保持在距第一个部件 80 格以内。

目前有两个选项只在 Kotlin 中提供:salt,一个数字,决定结构落在每 spacing 个区块的范围内的哪个位置(spacing 和 salt 都相同的两个结构会落在相同的位置;不设置时,由结构的 id 生成一个);以及 aboveBottom,它的部件必须保持在世界底部之上多少格(在某处会延伸到更低的地方,结构就不会生成)。

由你的代码逐块建造的结构见用代码生成页面。

生物群系

自定义生物群系会占据游戏生物群系的一部分位置。那里生长的、生活的和被挖出的内容都会保留,同时带有自己的气候和颜色。

content/ruby_meadow.craftCraftScript
biome "ruby_meadow" {
    replaces: "minecraft:plains"
    share: 0.5
    temperature: 0.8
    downfall: 0.4
    precipitation: true
    grass_color: "#d05070"
    foliage_color: "#c04060"
    sky_color: "#ff9fb0"
    fog_color: "#ffd0d8"
    water_color: "#ff6080"
    water_fog_color: "#a03050"
}
属性 取值 默认值
replaces 它占据其位置的游戏生物群系:"minecraft:plains" "minecraft:plains"
share 它占据这些位置的比例,大于 0 且最大为 1 0.5
temperature 气候的温度 0.8
downfall 降水量 0.4
precipitation 下雨为 true,不下雨为 false true
grass_color 草的颜色,#rrggbb 被替换生物群系的值
foliage_color 树叶的颜色 被替换生物群系的值
sky_color 天空的颜色 被替换生物群系的值
fog_color 雾的颜色 被替换生物群系的值
water_color 水的颜色 #3f76e4
water_fog_color 水下雾的颜色 被替换生物群系的值

share 会均匀分布在被替换生物群系原本所在的位置:0.5 占一半,即每两处取一处。新生物群系继承那里生长的、生活的和被挖出的内容(植被、矿石、洞穴、生物、音乐),以及其他模组添加到该生物群系的内容。未指定的颜色保持为被替换生物群系的颜色。

多个生物群系可以替换同一个生物群系,它们会分享其位置。用 /locate biome <模组 id>:ruby_meadow 找到你的生物群系。

声音、地面与生物

生物群系还可以有自己的音乐和氛围,用你的方块覆盖它的地面,并选择谁住在那里:

content/glow_marsh.craftCraftScript
biome "glow_marsh" {
    replaces: "minecraft:swamp"
    share: 0.4
    music: "mymod:music.marsh"
    ambient_sound: "mymod:ambient.marsh_hum"
    particle: { id: "minecraft:glow", chance: 0.005 }
    surface: "mymod:glow_moss"
    spawns: [
        { creature: "minecraft:frog", weight: 10, group: [2, 5] },
        { creature: "mymod:marsh_wisp", weight: 4, group: [1, 2] },
    ]
}
属性 取值 默认值
music 一个声音 id,作为该生物群系的音乐播放 被取代生物群系的
ambient_sound 在那里持续响着的声音(风声、嗡嗡声) 被取代生物群系的
particle { id, chance }:飘浮在它空气中的粒子;chance 是每刻玩家附近每个位置的概率(0.001 是少量) 被取代生物群系的;chance 0.01
surface 覆盖它地面的方块(每层地面最上面的方块) 维度通常的地面
spawns [{ creature, weight, group }]:住在那里的生物;一旦指定任何一个,就只有这些而没有其他 被取代生物群系的;weight 10,group [1, 4]
inherits false:只有给予这个生物群系的内容才会在那里生成(它的 features、它的 carvers、模组放在其中的东西),而不包括在被取代生物群系中生成的内容 true
features [{ id, step }]:已经存在的地物,游戏的或数据的,放置在其中。step 是 lakes、surface、ores、underground 或 vegetation —
carvers 挖空它洞穴的雕刻器的 id,你的或游戏的;它们按给定顺序运行,先于被取代生物群系的 —

使用 inherits: false 时,该生物群系仍然占据被取代生物群系的位置,除非你另外指定,否则也保留它的生物和声音;只是那里生长和被挖空的东西不再随之而来。逐块生成的地面和你自己的雕刻器见用代码生成页面。

末地的生物群系

在末地,生物群系占据的是整片约 200 格宽的区域,而不是按气候划分的位置。取代 minecraft:end_highlands 时,它占据其区域内所有的岛屿陆地(高地、中地和荒地);取代 minecraft:small_end_islands 时,占据岛屿之间的虚空。中央岛仍属于游戏,区域在每个世界中都不同。

content/crystal_glade.craftCraftScript
biome "crystal_glade" {
    replaces: "minecraft:end_highlands"
    share: 0.3
    sky_color: "#2a1840"
    fog_color: "#3c2a5a"
    surface: "mymod:crystal_moss"
    inherits: false
    features: [{ id: "minecraft:end_gateway_return", step: "surface" }]
}

注意事项

  • 仅限新区块。 已生成的区块不会改变;只有安装模组之后生成的区块才会出现矿石、树木、植物、结构和生物群系。探索新的地形来找到它们。
  • 不存在的名称会被忽略。 biomes 中不存在的生物群系或标签(比如另一个未安装的模组的)会被跳过,而不是报错。
  • 没有地面就没有结构。 立在地表的结构不会生成在该列完全没有地面的地方,比如末地岛屿之间的虚空。
  • 用代码编写的地物、结构、地形、生物群系地图、雕刻器和地面见用代码生成页面。
  • 完整的自定义世界见维度页面。

Kotlin 中

content/World.ktKotlin
ore("ruby_ore") {
    replaces = OreHost.STONE_AND_DEEPSLATE
    size = 9
    perChunk = 8
    minY = -60
    maxY = 60
}

ore("void_ore") {
    replaces = OreHost.END_STONE
    biome("mymod:crystal_glade")
}

tree("ruby_tree") {
    trunk = "ruby_log"
    leaves = "ruby_leaves"
    height = 6
    perChunk = 2
    biome("#minecraft:is_forest")
}

block("ruby_flower") { plant = true }

patch("ruby_flowers") {
    block = "ruby_flower"
    rarity = 4
    biome("#minecraft:is_overworld")
}

structure("old_ruins") {
    variant("ruins_small", weight = 3)
    variant("ruins_tower")
    biome("#minecraft:is_forest")
    spacing = 20
    offsetY = -2
    skip("minecraft:air")
    replace("minecraft:stone_bricks", "minecraft:cracked_stone_bricks", chance = 0.3)
    decay(0.8, "minecraft:stone_bricks", "minecraft:cracked_stone_bricks")
    salt = 120457
}

structure("hamlet") {
    biome("minecraft:plains")
    spacing = 34
    depth = 6
    pool("hamlet/square") { piece("hamlet/square") }
    pool("hamlet/streets") {
        piece("hamlet/street_straight", weight = 3, followsGround = true)
        piece("hamlet/street_corner", followsGround = true)
        fallback = "hamlet/street_ends"
        replace("minecraft:stone_bricks", "minecraft:oak_planks", where = "minecraft:water")
    }
    pool("hamlet/street_ends") { piece("hamlet/street_end", followsGround = true) }
    pool("hamlet/houses") {
        piece("hamlet/house_small", weight = 4)
        piece("hamlet/house_big")
        none = 2
    }
}

biome("glow_marsh") {
    replaces = "minecraft:swamp"
    share = 0.4
    music = "mymod:music.marsh"
    ambientSound = "mymod:ambient.marsh_hum"
    particle("minecraft:glow", chance = 0.005)
    surface = "mymod:glow_moss"
    spawn("minecraft:frog", weight = 10, group = 2..5)
}

biome("crystal_glade") {
    replaces = "minecraft:end_highlands"
    share = 0.3
    surface = "mymod:crystal_moss"
    inherits = false
    feature("minecraft:end_gateway_return", FeatureStep.SURFACE)
}