API

Creatures

A custom creature uses the body of a game creature (its model, animations, size and way of moving) with your texture painted on top, your stats, your behavior and your loot. You declare it like the rest of your content: a list of properties.

Declare a creature

content/creatures.craftCraftScript
creature "ghost" {
    body: "minecraft:zombie"
    texture: "entity/ghost"
    health: 40
    speed: 0.3
    attack: 4
    behaviour: ["swim", "melee_attack", { wander: 0.8 }, { look_at_players: 10 }, "look_around"]
    targets: ["hurt_by", "minecraft:pig"]
    loot: [ { item: "ghostmod:ecto", count: [1, 2] } ]
}
Property Values Default
body id of a game creature: minecraft:zombie, minecraft:wolf, minecraft:cow… required
texture texture path, painted over the body's model: entity/ghost is the mod's textures/entity/ghost.png the body's look
model your own 3D model: entity/robot is the mod's models/entity/robot.json (below) the body's model
size [width, height] in blocks: what it collides with and where it is clicked (below) the body's
health health points the body's
speed walking speed (a zombie moves at 0.23, a wolf at 0.3) the body's
attack melee attack damage the body's
follow_range how many blocks away it detects its targets the body's
behaviour list of behaviors, from most to least important (below) the body's behavior
targets who it attacks, in priority order (below) nobody
loot [{ item, count, chance }, …], same as for blocks: each item is rolled separately nothing
spawns biomes where it appears on its own, with weight and group size (below) it doesn't spawn on its own
breed_item item that makes two of them breed (below) can't be bred
tame_item item that tames it (below) can't be tamed
rideable true lets players ride it (below) false

Behaviors

The list is the priority: when two want to act at once, the earlier one wins. Write each one as a name, as { name: number } for its main setting, or as a map with all its settings.

Behavior What it does Settings
"swim" Stays afloat in water.
"look_around" Looks around when idle.
"wander", { wander: 0.8 } Wanders around. speed, default 1
"look_at_players", { look_at_players: 10 } Looks at nearby players. range in blocks, default 8
"melee_attack", { melee_attack: 1.2 } Chases and hits its target. speed, default 1
"panic", { panic: 1.5 } Runs away when it takes damage. speed, default 1.25
{ avoid: "minecraft:wolf", distance: 8, speed: 1.2 } Keeps away from that creature. distance in blocks, speed
{ follow: "minecraft:wheat", speed: 1 } Follows whoever holds that item. speed
{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 } Shoots a projectile at its target (below). projectile, interval, range, speed, power, damage
"breed", { breed: 1 } Breeds with another of its kind once fed. speed
"follow_parent", { follow_parent: 1.1 } A baby stays close to its parent. speed
{ follow_owner: 1.2, distance: 8 } A tamed creature follows its owner. speed, distance in blocks
{ fly_around: 1 } Flies around; for flying bodies (parrot, bee, allay). speed
{ can_start: …, tick: … } Your own code (below). can_start, tick, keep_going, moves

Speed numbers multiply the creature's base speed: { wander: 0.8 } wanders a bit slower than it walks.

Targets

targets says who it attacks, from highest to lowest priority. "hurt_by" is whoever damages it; an entity id is the nearest entity of that kind ("minecraft:player" for players, "minecraft:pig" for pigs). For the attack to happen, the creature needs a behavior that performs it, such as "melee_attack".

Spawn it

commands/haunt.craftCraftScript
command haunt requires "ghostmod.haunt" describe "Summon a ghost where you stand" {
    if player == null { return "Players only" }
    world().spawn("ghostmod:ghost", player.location)
    return "&7A ghost appears…"
}

The id is your mod's: ghostmod:ghost. The game command /summon ghostmod:ghost works too.

Appear on their own

Add spawns and the creature shows up in the world by itself, like the game's own creatures.

content/ghost.craftCraftScript
creature "ghost" {
    body: "minecraft:zombie"
    spawns: [ { biomes: ["minecraft:dark_forest", "#minecraft:is_forest"], weight: 20, group: [1, 2] } ]
}

Each entry lists the biomes where it appears (an id, or a #tag for a whole group of biomes), a weight (how often, compared with the other creatures of that biome: a cow is 8, a zombie 95) and a group size range.

It follows the spawning rules of its body: with a zombie body it appears in the dark, with a cow body on grass in the light. This holds both when the world is generated and later on.

Spawn egg. Every creature also gets a spawn egg, its id plus _spawn_egg (ghost_spawn_egg), in the creative spawn eggs tab. Its texture is the mod's textures/item/ghost_spawn_egg.png if there is one; otherwise it looks like its body's egg.

In Kotlin:

content/Creatures.ktKotlin
content.creature("ghost") {
    body = "minecraft:zombie"
    spawn("minecraft:dark_forest", "#minecraft:is_forest", weight = 20, group = 1..2)
}

Steer a creature from code

The creatures you declare, and any other mob, are a Creature. From an event, a command or a timer you can give them orders:

Member What it does
target Who it is attacking. Read it or set it.
moveTo(location, speed), move_to Walks to a location. The speed multiplies its normal speed.
stop() Stops walking.
isMoving, is_moving Whether it is walking somewhere.
lookAt(location), look_at Turns to look at a location.
canSee(entity), can_see Whether it has a clear line of sight to that entity.
isBaby Whether it is a baby.
ownerId The id of its owner, once tamed.
tame(player) Makes that player its owner.
isSitting Whether it is sitting.
playAnimation(name), play_animation Plays one of its model's animations (see below).

Every entity has a velocity, a Vector in blocks per tick. Read it or set it to push the entity: entity.velocity = vector(0, 0.6, 0) sends it upwards.

In CraftScript, a typed let narrows an entity to a creature. It is checked when it runs:

commands/pup.craftCraftScript
command pup requires "dogmod.pup" describe "Get a pet dog" {
    if player == null { return "Players only" }
    let dog: Creature = world().spawn("dogmod:dog", player.location)
    dog.tame(player)
    dog.look_at(player.location)
    dog.velocity = vector(0, 0.6, 0)      // a little hop
    return "&7Good dog!"
}

Behaviours of your own

When the ready-made ones aren't enough, write one with your own code. It goes in the behaviour list like any other, so its position is its priority.

content/herder.craftCraftScript
creature "herder" {
    body: "minecraft:wolf"
    targets: ["minecraft:pig"]
    behaviour: [
        "swim",
        {
            can_start: fun(c) { return c.target != null && c.can_see(c.target) },
            tick: fun(c) {
                if c.target != null {
                    c.look_at(c.target.location)
                    if !c.is_moving { c.move_to(c.target.location, 1.2) }
                }
            }
        },
        { wander: 1 }
    ]
}
  • can_start: asked every few ticks; return whether it should begin.
  • tick: runs every tick while the behaviour is active.
  • keep_going: says whether to carry on. If omitted, it is the same as can_start.
  • moves: true by default. A moving behaviour doesn't run together with another moving one; the earlier in the list wins. Use false for behaviours that only look or react.

In Kotlin: Behaviour.Custom(canStart = { … }, tick = { … }, keepGoing = …, moves = true)

Animals: breed, tame, ride and fly

A few properties and behaviours turn a creature into an animal. This one breeds with apples, is tamed with a bone and can be ridden:

content/mooble.craftCraftScript
creature "mooble" {
    body: "minecraft:cow"
    tame_item: "minecraft:bone"
    breed_item: "minecraft:apple"
    rideable: true
    behaviour: ["swim", { breed: 1 }, { follow_parent: 1.1 }, { follow_owner: 1.2, distance: 8 }, { wander: 0.8 }]
}
  • Breed. breed_item plus the "breed" behaviour: fed that item, two of the same kind make a baby of that kind. It needs an animal body (cow, wolf, chicken…). Babies use "follow_parent" to stay close to their parents.
  • Tame. tame_item: each try works one time in three. The owner is remembered, and a tamed creature never targets its owner. Sneak and use with an empty hand to make it sit or stand up. { follow_owner: 1.2, distance: 8 } makes it follow its owner.
  • Ride. rideable: true: use with an empty hand to climb on (once tamed, only its owner can). Steer it with the movement keys and the mouse; sneak to get off.
  • Fly. { fly_around: 1 } for flying bodies (parrot, bee, allay). A rideable creature with a flying body climbs when its rider looks up and dives when the rider looks down.

In Kotlin: breedItem = …, tameItem = …, rideable = true and the behaviours Behaviour.Breed(), Behaviour.FollowParent(), Behaviour.FollowOwner(speed, distance) and Behaviour.FlyAround().

Projectiles of your own

Besides the game's projectiles, a mod can declare its own. It has its own damage, gravity and what happens when it lands:

content/frost_bolt.craftCraftScript
projectile "frost_bolt" {
    damage: 4
    gravity: false
    on_hit: fun(e) {
        let at = e.entity?.location ?? e.block?.location
        if at != null { e.projectile.world.spawn_particle("minecraft:snowflake", at.add(0.5, 1, 0.5), 12, 0.4, 0.02) }
    }
}
  • damage: damage dealt to what it hits.
  • gravity: false makes it fly in a straight line.
  • on_hit: fun(e), called when it lands, with the same e as the hit event: e.projectile, e.shooter and e.entity or e.block.
  • texture: the projectile flies looking like its item, so by default it uses textures/item/<id>.png; set texture to use another.

Use it like any other projectile: world.shoot("mymod:frost_bolt", from, velocity, shooter), or in a creature, { ranged_attack: "mymod:frost_bolt" }. In Kotlin:

content/FrostBolt.ktKotlin
content.projectile("frost_bolt") {
    damage = 4.0
    gravity = false
    onHit = { e -> /* … */ }
}

Ranged attacks and projectiles

The ranged_attack behaviour makes a creature shoot. It closes in until its target is in range and in sight, then fires.

content/frost_spirit.craftCraftScript
creature "frost_spirit" {
    body: "minecraft:snow_golem"
    behaviour: [{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 }, { wander: 0.6 }]
    targets: ["minecraft:zombie", "hurt_by"]
}

on hit(e) {
    if e.shooter?.type == "frostmod:frost_spirit" && e.entity != null {
        e.entity.velocity = vector(0, 0.2, 0)      // knocked up
    }
}
Setting Meaning
ranged_attack ranged_attack: the projectile (minecraft:arrow, minecraft:snowball, minecraft:small_fireball, minecraft:wind_charge…).
interval interval: ticks between shots.
range range: distance in blocks from which it shoots.
speed speed: walking speed while it closes in.
power power: how fast the projectile flies.
damage damage: extra damage when it hits.

To shoot from code, for creatures or for a player's ability, use world().shoot(projectile, from, velocity, shooter, damage).

In Kotlin:

content/FrostSpirit.ktKotlin
creature("frost_spirit") {
    body = "minecraft:snow_golem"
    behaviour(Behaviour.RangedAttack(Identifier.parse("minecraft:snowball"), interval = 20, range = 14.0, damage = 3.0))
    target(TargetRule.Nearest(Identifier.parse("minecraft:zombie")))
}

// From code, e.g. a player's ability:
world.shoot(Identifier.parse("minecraft:small_fireball"), from, Vector(0.0, 0.0, 1.5), shooter = player, damage = 4.0)

The hit event (ProjectileHitEvent) fires when a projectile lands. It has e.projectile, e.shooter and either e.entity or e.block, whichever it hit. Cancel it and the projectile goes through. See Events.

Good to know

  • Each body has its limits. Some game creatures lack a stat: a cow has no attack damage. If you set it, it is ignored and Crafting warns in the log.
  • Bodies that don't walk. Bodies that can't find a path on their own ignore walking behaviors (wander, chase, panic, follow, avoid); the rest work.
  • Without behaviour, the creature behaves like the body it uses. As soon as you write the list, it only does what is in it.
  • The model only changes the look. Movement and behavior always come from the body, and so does its size unless you set size.
  • A custom creature is a living entity like any other: it receives the spawn, damage and entity_death events, has data, and you handle it with what's in The world and players.

In Kotlin

content/Creatures.ktKotlin
package com.example.rubies.content

object Creatures {
    fun register(content: ContentBuilder) {
        content.creature("ruby_golem") {
            body = "minecraft:zombie"
            texture = "entity/ruby_golem"
            health = 60.0
            speed = 0.25
            attackDamage = 6.0
            behaviour(Behaviour.Swim)
            behaviour(Behaviour.MeleeAttack())
            behaviour(Behaviour.Wander(0.6))
            behaviour(Behaviour.LookAtPlayers(12f))
            behaviour(Behaviour.LookAround)
            target(TargetRule.HurtBy)
            target(TargetRule.Nearest(Identifier.parse("minecraft:player")))
            drop(Items.RUBY, 2..4)
        }
    }
}

Behaviors: Behaviour.Swim, LookAround, Wander(speed), LookAtPlayers(range), MeleeAttack(speed), Panic(speed), Avoid(entity, distance, speed), Follow(item, speed). Targets: TargetRule.HurtBy and TargetRule.Nearest(id). Add loot with drop(item, count, chance), same as for blocks.

Your own 3D model

Add model and the creature is drawn with your model instead of the body's. The body still gives it its size, movement and behavior; the model only changes how it looks.

content/robot.craftCraftScript
creature "robot" {
    body: "minecraft:pig"
    model: "entity/robot"
    texture: "entity/robot"
    behaviour: ["swim", { wander: 0.8 }, { look_at_players: 10 }]
}

The model is the mod's models/entity/robot.json and its texture is textures/entity/robot.png. In Kotlin: creature("robot") { body = "minecraft:pig"; model = "entity/robot"; texture = "entity/robot" }. The format is the same across game versions.

models/entity/robot.jsonJSON
{ "texture_size": [64, 64],
  "bones": [
    { "name": "body", "pivot": [0, 6, 0], "cubes": [ { "origin": [-5, 6, -3], "size": [10, 10, 6], "uv": [0, 0] } ] },
    { "name": "head", "parent": "body", "pivot": [0, 16, 0], "cubes": [ { "origin": [-4, 16, -4], "size": [8, 8, 8], "uv": [0, 16] } ] },
    { "name": "left_leg", "pivot": [2.5, 6, 0], "cubes": [ { "origin": [1, 0, -1.5], "size": [3, 6, 3], "uv": [32, 0] } ] },
    { "name": "right_leg", "pivot": [-2.5, 6, 0], "cubes": [ { "origin": [-4, 0, -1.5], "size": [3, 6, 3], "uv": [32, 16] } ] }
  ],
  "animations": { "head": "look", "left_leg": "walk", "right_leg": "walk_opposite", "body": "idle" } }

Sizes are in pixels (16 is one block), with y pointing up from the creature's feet.

  • Bone: name, an optional parent (it moves with it), pivot (the point it turns around) and cubes.
  • Cube: origin (its lowest corner), size and uv (where its texture starts in the image). The layout is the usual box one: a cube of width w, height h and depth d uses an area of 2(w+d) by (d+h) pixels.
  • Animations go per bone, as a name or a list of names:
Animation What it does
look Turns where the creature looks; for heads.
walk, walk_opposite Swings while walking; the second in opposite phase, for the other limb.
idle A slight sway.
spin Keeps turning.

If a model can't be drawn, the creature looks like its body and the log says why.

Size

A creature is as big as its body. When your model is smaller or bigger, set size: [width, height] in blocks so what it collides with and where it is clicked match what players see:

content/drake.craftCraftScript
creature "drake" {
    body: "minecraft:bee"
    model: "entity/drake"
    texture: "entity/drake"
    size: [1.4, 1.1]
}

In Kotlin: size = 1.4 to 1.1.

Keyframed animations

For movement beyond the ready-made animations, add a clips section next to bones and animations:

models/entity/robot.jsonJSON
{ "bones": [ … ],
  "animations": { … },
  "clips": {
    "idle": { "length": 2.0, "bones": { "head": { "position": { "0": [0, 0, 0], "1": [0, 0.6, 0], "2": [0, 0, 0] } } } },
    "walk": { "length": 1.0, "bones": { "body": { "rotation": { "0": [0, 0, -6], "0.5": [0, 0, 6], "1": [0, 0, -6] } } } },
    "roar": { "length": 1.5, "bones": {
      "head": { "rotation": { "0": [0, 0, 0], "0.4": [-35, 0, 0], "1.1": [-35, 0, 0], "1.5": [0, 0, 0] },
                "scale": { "0": 1, "0.4": 1.35, "1.1": 1.35, "1.5": 1 } },
      "body": { "position": { "0": [0, 0, 0], "0.3": [0, 4, 0], "0.6": [0, 0, 0] } } } }
  } }
  • Each clip has a length in seconds and, per bone, up to three channels: rotation in degrees, position in pixels (y up) and scale (a number or [x, y, z]).
  • The keys are times in seconds. Values blend in a straight line between keys; add "interpolation": "smooth" to a clip and it passes through its keys on a curve instead. A channel may also be a single value for the whole clip.
Clip When it plays
walk Loops while it walks, in step with its feet.
idle Loops while it stands still.
attack Plays as it strikes.
Any other name Plays when code asks: c.play_animation("roar"). Nearby players who have Crafting see it.
Smooth clipJSON
"idle": { "length": 1.2, "interpolation": "smooth", "bones": {
  "left_wing": { "rotation": { "0": [0, 0, -20], "0.6": [0, 0, 25], "1.2": [0, 0, -20] } } } }

A clip started from code plays over walk, idle and attack instead of replacing them, and they add up: a creature can roar while it walks.

Babies of a creature with its own model are drawn at half size.

Textures

Without a model, the texture has the same layout as the chosen body's (a zombie, a wolf…), so you can start from a template for that body and paint over it. It goes inside the mod, like item and block textures:

Mod Where
CraftScript (folder) mods/ghostmod/textures/entity/ghost.png
Kotlin or Java src/main/resources/assets/ghostmod/textures/entity/ghost.png