API
Blöcke, Gegenstände und Rezepte
Du deklarierst Inhalte als Liste von Eigenschaften. Crafting erstellt die echten Spielobjekte zum richtigen Zeitpunkt und erzeugt die Modelle, Beutetabellen, Tags, Rezepte und Übersetzungen, die sie brauchen. Du schreibst keine Datendateien des Spiels.
Gegenstände
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" }| Eigenschaft | Werte | Standard |
|---|---|---|
stack oder max_stack |
1–99 | 64 |
rarity |
common, uncommon, rare, epic (Farbe des Namens) |
common |
glint |
Verzauberungsglanz | false |
durability |
Zahl, erzwingt stack: 1 |
0 |
fireproof |
verbrennt nicht in Lava oder Feuer | false |
food |
Zahl oder { nutrition, saturation, always_edible } |
— |
tool |
"pickaxe" oder { type, tier, damage, speed }; type: pickaxe, axe, shovel, hoe, sword |
— |
tab |
Tab im Kreativinventar (siehe unten) | ingredients |
texture |
Pfad der Textur | item/<id> |
on_use |
fun(player, stack); gibt true zurück, wenn sie die Benutzung übernimmt |
— |
armor |
{ slot, defense, toughness, knockback_resistance, texture } |
— |
on_hit |
fun(attacker, target, stack) |
— |
cooldown |
Sekunden | 0 |
Rüstung, Waffen und Abklingzeiten
Ein Gegenstand mit armor lässt sich tragen. slot ist head, chest, legs oder feet. defense, toughness und knockback_resistance (0 bis 1) sind die Rüstungswerte des Spiels: Ein Eisenbrustpanzer hat 6 Verteidigung, Diamant 2 Härte.
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 bestimmt, wie sie getragen aussieht. frost bedeutet textures/entity/equipment/humanoid/frost.png in der Mod und für die Beine textures/entity/equipment/humanoid_leggings/frost.png, mit demselben Aufbau wie die Rüstungstexturen des Spiels. Ohne sie sieht die Rüstung wie Eisen aus.
on_hit läuft, wenn der Gegenstand zum Schlagen benutzt wird, von einem Spieler oder einem 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 (in Sekunden) sperrt den Gegenstand nach jeder Benutzung, die sein on_use übernommen hat, für diese Zeit; die Schnellleiste zeigt es an. Aus dem Code: player.set_cooldown("minecraft:feather", 2) und player.has_cooldown(id). In Kotlin: armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") für Rüstung und onHit = { ctx -> … } für Waffen (ctx.attacker, ctx.target, ctx.stack).
item("frost_helmet") { armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") }Blöcke
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] }| Eigenschaft | Werte | Standard |
|---|---|---|
hardness |
Zahl; -1 = unzerstörbar |
1.5 |
resistance |
Explosionswiderstand | = hardness |
light |
0–15 | 0 |
light_when |
ein Schalter-Zustand wie "lit": Der Block leuchtet nur, solange er an ist (siehe Blockzustände) |
immer |
sound |
stone, wood, gravel, grass, metal, glass, wool, sand | stone |
like |
ein Block, nach dessen Vorbild er gemacht wird, aus dem Spiel ("minecraft:end_stone") oder von dir: Härte, Explosionswiderstand, Geräusch und ob das richtige Werkzeug nötig ist, damit etwas fällt, sind die dieses Blocks |
— |
kind |
eine der Arten des Spiels: Treppe, Tür, Truhe, Nutzpflanze… (siehe Blockarten) | cube |
tool, tier |
Werkzeug, das nötig ist, damit der Block etwas fallen lässt, und das Mindestmaterial dieses Werkzeugs: wood, stone, iron, diamond, netherite | —, wood |
drops, drop_count |
ein einzelner Gegenstand: ID; Zahl oder [min, max] |
der Block selbst, 1 |
loot |
mehrere Gegenstände: [{ item, count, chance, tool, fortune }, …]. Jeder wird einzeln ausgewürfelt; count ist eine Zahl oder [min, max], chance geht von 0 bis 1 (Standard 1). tool und fortune: siehe Drops weiter unten |
— |
friction |
0.6 normal, 0.98 Eis | 0.6 |
transparent |
man kann hindurchsehen (Glas, Laub) | false |
has_item |
auch den Gegenstand des Blocks registrieren; ohne einen lässt der Block nichts fallen, außer drops oder loot sagen, was |
true |
tab |
Tab im Kreativinventar | building_blocks |
on_use |
fun(player, block); gibt true zurück, wenn sie die Benutzung übernimmt. Ein Spieler, der sich duckt und etwas in der Hand hält, platziert es stattdessen, wie bei den Blöcken des Spiels |
— |
Tabs: building_blocks, colored_blocks, natural_blocks, functional_blocks, redstone_blocks, tools_and_utilities, combat, food_and_drinks, ingredients, spawn_eggs. In Kotlin stehen sie in CreativeTabs.
Damit eine Kreatur beim Tod zusätzlich etwas fallen lässt, füg es im Event entity_death zu e.drops hinzu (siehe Events).
Blöcke, die keine Würfel sind, Blöcke der Arten des Spiels, Kreaturen, Erze, Geräusche und Partikel werden genauso deklariert und haben eigene Seiten: Blockformen, Blockarten, Kreaturen, Weltgenerierung, Geräusche und Partikel.
Drops: Werkzeuge und Glück
Ein Drop in loot kann ein Werkzeug verlangen, und Glück kann mehr davon geben:
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": Er fällt nur, wenn der Block mit Behutsamkeit abgebaut wird.tool: "shears": nur mit einer Schere oder Behutsamkeit, so wie die Blätter und Ranken des Spiels sich selbst fallen lassen.- Wenn manche Drops ein Werkzeug verlangen, fallen die Drops ohne eines nur, wenn dieses Werkzeug nicht benutzt wird: Das Erz oben lässt sich selbst mit Behutsamkeit fallen und sonst seine Rubine.
fortune: true: Glück gibt mehr davon, mehr von seinemcountwie bei den Erzen des Spiels oder eine besserechancewie bei Setzlingen aus Blättern.
In Kotlin: drop("ruby", 1..2, tool = null, fortune = true) und 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)
}Rezepte
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 }Verhalten beim Benutzen
on_use ist die einzige Logik, die neben den Daten steht. Wird sie länger als ein paar Zeilen, schreib die Funktion in lib/ und importiere sie:
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
}Blöcke, die reagieren
Ein Block kann Code ausführen, wenn mit ihm oder in seiner Umgebung etwas geschieht. Jeder Handler erhält den Block und, wo es passt, die Entität.
| Eigenschaft | Läuft, wenn |
|---|---|
on_step: fun(block, entity) |
eine Entität auf den Block tritt. |
on_inside: fun(block, entity) |
eine Entität sich im Block befindet. Der Block muss Entitäten durchlassen: ein Portal, eine Pflanze oder eine nicht feste Form. |
on_random_tick: fun(block) |
hin und wieder zufällig, wie wachsende Feldfrüchte: etwa alle 68 Sekunden pro Block. |
on_tick: fun(block) |
die mit block.schedule(ticks) angeforderte Zeit verstrichen ist (20 Ticks = 1 Sekunde). |
on_neighbor_change: fun(block) |
sich ein benachbarter Block geändert hat: gesetzt, abgebaut oder mit Signal versorgt. |
on_placed: fun(block, entity) |
jemand den Block platziert hat; entity ist die Person. |
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) }
}In Kotlin heißen die Handler onStep, onInside, onRandomTick, onTick, onNeighborChange und onPlaced. Jeder erhält einen BlockEventContext mit block und, falls vorhanden, entity:
block("pad") { onStep = { ctx -> ctx.entity?.velocity = Vector(0.0, 1.1, 0.0) } }Portale
Ein Block mit portal bringt jeden, der darin steht, in eine andere Dimension. Gib die Zieldimension an, oder to und back, um zu wählen, wohin es von dort aus zurückgeht:
block "sky_gate" { portal: "gatemod:sky", light: 11 }
block "sky_gate_two_way" { portal: { to: "gatemod:sky", back: "minecraft:overworld" } }Die Entität landet am Boden bei denselben x und z und findet dort einen Portalblock für den Rückweg; ist keiner in der Nähe, wird neben der Ankunft einer gesetzt. Entitäten warten einen Moment, bevor sie ein Portal erneut nutzen. Portalblöcke lassen Entitäten durch. Die Dimension selbst deklarierst du mit dimension (siehe @dim@). In Kotlin:
block("sky_gate") { portal("gatemod:sky") }Blockzustände
states gibt einem Block Werte, die er sich merkt und anzeigen kann:
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": Nord, Ost, Süd oder West. Er wird zum platzierenden Spieler ausgerichtet, und das Modell dreht sich mit."lit": Ein Name allein ist ein Ein/Aus-Schalter, standardmäßig aus.{ lit: true }macht ihn standardmäßig an.{ stage: 3 }: eine Zahl von 0 bis zum angegebenen Maximum (1 bis 15)."facing_all": wie"facing", aber auch nach oben und unten. Er wird zum platzierenden Spieler ausgerichtet, und das Modell wird für oben und unten gekippt.{ shape: ["top", "middle", "bottom"] }: einer von wenigen Namen, standardmäßig der erste. Namen bestehen aus Kleinbuchstaben, Ziffern und_.
Lesen und ändern kannst du sie mit block.state("lit") und block.set_state("lit", true). Das funktioniert auch bei den Blöcken des Spiels, etwa facing und lit eines Ofens.
Ein vom Standard abweichender Zustand kann anders aussehen. Lege eine Textur unter textures/block/<id>_<name>.png (ein eingeschalteter Schalter) oder <id>_<name><n>.png (eine Zahl) oder <id>_<value>.png (ein Name) ab, oder ein Modell unter models/block/<id>_<name>.json. Zuerst wird die ganze Kombination (<id>_lit_stage2) versucht, dann jeder Zustand einzeln. Im Beispiel sind lamp.json und lamp_lit.json die zwei Modelle der Mod, etwa mit dem Parent minecraft:block/orientable und Texturen für Vorderseite, Seite und Oberseite.
Licht je nach Zustand. light_when: "lit" lässt das light des Blocks nur leuchten, solange dieser Schalter an ist, wie bei einer Lampe:
block "lamp" {
light: 15
light_when: "lit"
states: ["lit"]
on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
}In Kotlin: facing(), facingAll(), toggle("lit"), number("stage", 3), choice("shape", "top", "middle", "bottom") sowie block.state(…) und block.setState(…) im Code:
block("lamp") {
model = "block/lamp"
facing()
toggle("lit")
number("stage", 3)
}Effekte
Ein Mod kann eigene Statuseffekte hinzufügen. Sie erscheinen wie die des Spiels auf dem Bildschirm des Spielers und werden auf dieselbe Weise vergeben und gelesen.
effect "frozen" {
color: "#88CCFF"
kind: "harmful"
on_tick: fun(e, level) { e.damage(level) }
}color: die Farbe seiner Partikel und des Hintergrunds seines Symbols.kind:beneficial,harmfuloderneutral.on_tick:fun(e, level), läuft jede Sekunde, solange eine Entität den Effekt hat, mit dessen Stufe (1 ist I).- Das Symbol ist
textures/mob_effect/<id>.pngder Mod.
Vergib ihn mit e.add_effect("mymod:frozen", 3, 1) (Id, Sekunden, Stufe) und prüfe ihn mit e.has_effect("mymod:frozen"), wie bei den Effekten des Spiels (siehe Die Welt und die Spieler). In 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 lässt den Block ein Signal abgeben, so stark wie sein Zustand power, eine Zahl von 0 bis 15, die automatisch hinzukommt. b.set_state("power", 15) schaltet ihn ein, und Leitungen, Lampen und Türen ringsum folgen. block.redstone_power ist die Stärke, die der Block aus der Umgebung erhält, und on_neighbor_change läuft, wenn sie sich ändert (die Lampe oben nutzt das).
In Kotlin: redstone = true und block.redstonePower.
block("battery") {
texture = "minecraft:block/copper_block"
redstone = true
// in a handler: ctx.block.redstonePower
}Inventare
block "crate" { texture: "minecraft:block/barrel_side", inventory: 27 }inventory: 27 gibt dem Block so viele Plätze (9 bis 54, in Reihen zu 9). Er behält seine Gegenstände über Neustarts hinweg, beim Benutzen öffnet sich eine Truhenoberfläche (es sei denn, sein on_use hat den Klick übernommen), und beim Abbauen fällt der Inhalt heraus. Bei 1 bis 8 Plätzen gibt es keine Oberfläche: Die Mod legt Gegenstände selbst hinein und nimmt sie heraus.
Im Code bietet block.inventory (funktioniert auch bei den Truhen und Fässern des Spiels) set(slot, item(…)), get(slot), count(id) und add(…). In Kotlin: inventory = 27.
block("crate") {
texture = "minecraft:block/barrel_side"
inventory = 27
}Gegenstände auf einem Block anzeigen. shows_item: 14 lässt den Gegenstand im ersten Platz des Blocks sich drehend über ihm schweben, so viele Pixel über seiner Unterseite. Jeder Spieler sieht ihn, und er verschwindet, wenn der Platz leer wird oder der Block abgebaut wird. Ein Podest nimmt den Gegenstand aus der Hand des Spielers und gibt ihn zurück:
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) legt einen Gegenstand in die Haupthand des Spielers oder leert sie mit null. In Kotlin: showsItem = 14 und 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
}
}Texturen
Texturen werden mit der Mod ausgeliefert, niemand muss ein Ressourcenpaket installieren. Der Dateiname ist die ID:
| Mod | Wo |
|---|---|
| CraftScript (Ordner) | mods/rubies/textures/item/ruby.png, mods/rubies/textures/block/ruby_ore.png |
| Kotlin oder Java | src/main/resources/assets/rubies/textures/item/ruby.png |
Liefert eine Mod in denselben Ordnern eigene Modelle, Rezepte oder Beutetabellen mit, nutzt Crafting diese.
In 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. Ohne Namensraum gehört eine ID deiner Mod ("ruby" ist rubies:ruby). Für Inhalte des Spiels schreibst du den Namensraum: "minecraft:diamond". Im Namensraum minecraft kannst du keine Inhalte registrieren.