API

Criaturas

Una criatura propia usa el cuerpo de una criatura del juego (su modelo, sus animaciones, su tamaño y su forma de moverse) con tu textura pintada encima, tus estadísticas, tu comportamiento y tu botín. Se declara como el resto del contenido: una lista de propiedades.

Declarar una criatura

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] } ]
}
Propiedad Valores Por defecto
body id de una criatura del juego: minecraft:zombie, minecraft:wolf, minecraft:cow… obligatoria
texture ruta de la textura, pintada sobre el modelo del cuerpo: entity/ghost es textures/entity/ghost.png del mod el aspecto del cuerpo
model tu propio modelo 3D: entity/robot es models/entity/robot.json del mod (abajo) el modelo del cuerpo
size [width, height] en bloques: con qué colisiona y dónde se le hace clic (abajo) el del cuerpo
health puntos de vida los del cuerpo
speed velocidad al andar (un zombi va a 0.23, un lobo a 0.3) la del cuerpo
attack daño de su ataque cuerpo a cuerpo el del cuerpo
follow_range a cuántos bloques detecta a sus objetivos el del cuerpo
behaviour lista de comportamientos, del más importante al menos (abajo) el comportamiento del cuerpo
targets a quién ataca, en orden de prioridad (abajo) a nadie
loot [{ item, count, chance }, …], como en los bloques: cada objeto se sortea por separado nada
spawns biomas donde aparece sola, con peso y tamaño de grupo (abajo) no aparece sola
breed_item objeto que hace que dos se reproduzcan (abajo) no se reproduce
tame_item objeto que la doma (abajo) no se puede domar
rideable true permite montarla (abajo) false

Comportamientos

La lista es la prioridad: cuando dos quieren actuar a la vez, gana el que va antes. Cada uno se escribe como un nombre, como { nombre: número } para su ajuste principal, o como un mapa con todos sus ajustes.

Comportamiento Qué hace Ajustes
"swim" Se mantiene a flote en el agua.
"look_around" Mira a su alrededor cuando no hace nada.
"wander", { wander: 0.8 } Pasea. speed, por defecto 1
"look_at_players", { look_at_players: 10 } Mira a los jugadores cercanos. range en bloques, por defecto 8
"melee_attack", { melee_attack: 1.2 } Persigue y golpea a su objetivo. speed, por defecto 1
"panic", { panic: 1.5 } Huye cuando recibe daño. speed, por defecto 1.25
{ avoid: "minecraft:wolf", distance: 8, speed: 1.2 } Se aleja de esa criatura. distance en bloques, speed
{ follow: "minecraft:wheat", speed: 1 } Sigue a quien lleve ese ítem en la mano. speed
{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 } Dispara un proyectil a su objetivo (abajo). projectile, interval, range, speed, power, damage
"breed", { breed: 1 } Se reproduce con otra de su especie cuando la han alimentado. speed
"follow_parent", { follow_parent: 1.1 } Una cría se queda cerca de su progenitor. speed
{ follow_owner: 1.2, distance: 8 } Una criatura domada sigue a su dueño. speed, distance en bloques
{ fly_around: 1 } Vuela de un lado a otro; para cuerpos voladores (loro, abeja, allay). speed
{ can_start: …, tick: … } Tu propio código (abajo). can_start, tick, keep_going, moves

Los números de velocidad multiplican la velocidad base de la criatura: { wander: 0.8 } pasea un poco más despacio de lo que anda.

Objetivos

targets dice a quién ataca, del más prioritario al menos. "hurt_by" es quien le haga daño; un id de entidad es el más cercano de esa clase ("minecraft:player" para los jugadores, "minecraft:pig" para los cerdos). Para que el ataque ocurra, la criatura necesita un comportamiento que lo ejecute, como "melee_attack".

Hacerla aparecer

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…"
}

El id es el de tu mod: ghostmod:ghost. También sirve el comando del juego /summon ghostmod:ghost.

Que aparezcan solas

Añade spawns y la criatura aparece sola en el mundo, como las criaturas del propio juego.

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

Cada entrada indica los biomes donde aparece (un id, o una #etiqueta para todo un grupo de biomas), un weight (peso: con qué frecuencia, comparado con las demás criaturas de ese bioma; una vaca tiene 8, un zombi 95) y un rango de tamaño de group (grupo).

Sigue las reglas de aparición de su cuerpo: con cuerpo de zombi aparece en la oscuridad, con cuerpo de vaca sobre la hierba y con luz. Vale tanto al generarse el mundo como después.

Huevo generador. Cada criatura recibe además un huevo generador, su id más _spawn_egg (ghost_spawn_egg), en la pestaña creativa de huevos generadores. Su textura es textures/item/ghost_spawn_egg.png del mod, si existe; si no, se ve como el huevo de su cuerpo.

En Kotlin:

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

Dirigir una criatura desde el código

Las criaturas que declaras, y cualquier otro mob, son una Creature. Desde un evento, un comando o un temporizador puedes darles órdenes:

Miembro Qué hace
target A quién ataca. Se puede leer y asignar.
moveTo(location, speed), move_to Camina hasta una ubicación. La velocidad multiplica su velocidad normal.
stop() Deja de caminar.
isMoving, is_moving Si está yendo a algún sitio.
lookAt(location), look_at Se gira para mirar una ubicación.
canSee(entity), can_see Si tiene línea de visión directa hacia esa entidad.
isBaby Si es una cría.
ownerId El id de su dueño, una vez domada.
tame(player) Hace que ese jugador sea su dueño.
isSitting Si está sentada.
playAnimation(name), play_animation Reproduce una de las animaciones de su modelo (ver abajo).

Toda entidad tiene una velocity, un Vector en bloques por tick. Léela o asígnala para empujar a la entidad: entity.velocity = vector(0, 0.6, 0) la lanza hacia arriba.

En CraftScript, un let con tipo reduce una entidad a criatura. Se comprueba al ejecutarse:

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!"
}

Comportamientos propios

Cuando los incluidos no bastan, escribe uno con tu propio código. Va en la lista behaviour como cualquier otro, así que su posición es su prioridad.

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: se consulta cada pocos ticks; devuelve si debe empezar.
  • tick: se ejecuta cada tick mientras el comportamiento está activo.
  • keep_going: indica si debe seguir. Si se omite, es igual que can_start.
  • moves: true por defecto. Un comportamiento que se mueve no corre a la vez que otro que también se mueve; gana el que va antes en la lista. Usa false para los que solo miran o reaccionan.

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

Animales: criar, domar, montar y volar

Unas pocas propiedades y comportamientos convierten una criatura en un animal. Esta se cría con manzanas, se doma con un hueso y se puede montar:

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 }]
}
  • Criar. breed_item más el comportamiento "breed": alimentadas con ese objeto, dos de la misma especie tienen una cría de esa especie. Necesita un cuerpo de animal (vaca, lobo, gallina…). Las crías usan "follow_parent" para no alejarse de sus progenitores.
  • Domar. tame_item: cada intento funciona una vez de cada tres. Se recuerda al dueño, y una criatura domada nunca ataca a su dueño. Agáchate y usa con la mano vacía para que se siente o se levante. { follow_owner: 1.2, distance: 8 } hace que siga a su dueño.
  • Montar. rideable: true: usa con la mano vacía para subirte (una vez domada, solo su dueño). Se dirige con las teclas de movimiento y el ratón; agáchate para bajar.
  • Volar. { fly_around: 1 } para cuerpos voladores (loro, abeja, allay). Una criatura rideable con cuerpo volador sube cuando su jinete mira hacia arriba y baja en picado cuando mira hacia abajo.

En Kotlin: breedItem = …, tameItem = …, rideable = true y los comportamientos Behaviour.Breed(), Behaviour.FollowParent(), Behaviour.FollowOwner(speed, distance) y Behaviour.FlyAround().

Proyectiles propios

Además de los proyectiles del juego, un mod puede declarar los suyos. Tiene su propio daño, su gravedad y lo que ocurre al caer:

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: daño que causa a lo que golpea.
  • gravity: false hace que vuele en línea recta.
  • on_hit: fun(e), se llama al caer, con el mismo e que el evento hit: e.projectile, e.shooter y e.entity o e.block.
  • texture: el proyectil vuela con el aspecto de su objeto, así que por defecto usa textures/item/<id>.png; con texture usas otra.

Úsalo como cualquier otro proyectil: world.shoot("mymod:frost_bolt", from, velocity, shooter), o en una criatura, { ranged_attack: "mymod:frost_bolt" }. En Kotlin:

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

Ataques a distancia y proyectiles

El comportamiento ranged_attack hace que una criatura dispare. Se acerca hasta que su objetivo está a tiro y a la vista, y entonces dispara.

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
    }
}
Ajuste Significado
ranged_attack ranged_attack: el proyectil (minecraft:arrow, minecraft:snowball, minecraft:small_fireball, minecraft:wind_charge…).
interval interval: ticks entre disparos.
range range: distancia en bloques desde la que dispara.
speed speed: velocidad al caminar mientras se acerca.
power power: la velocidad a la que vuela el proyectil.
damage damage: daño extra cuando acierta.

Para disparar desde el código, con criaturas o con la habilidad de un jugador, usa world().shoot(projectile, from, velocity, shooter, damage).

En 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)

El evento hit (ProjectileHitEvent) se lanza cuando un proyectil impacta. Tiene e.projectile, e.shooter y e.entity o e.block, según lo que haya alcanzado. Cancélalo y el proyectil lo atraviesa. Ver Eventos.

Lo que conviene saber

  • Cada cuerpo tiene sus límites. Algunas criaturas del juego no tienen una estadística: una vaca no tiene daño de ataque. Si la escribes, se ignora y Crafting lo avisa en el log.
  • Cuerpos que no andan. Los que no pueden buscar un camino por sí mismos ignoran los comportamientos de andar (pasear, perseguir, huir, seguir, evitar); los demás funcionan.
  • Sin behaviour, la criatura se comporta como el cuerpo que usa. En cuanto escribes la lista, solo hace lo que está en ella.
  • El modelo solo cambia el aspecto. El movimiento y el comportamiento siempre vienen del cuerpo, y el tamaño también, salvo que pongas size.
  • Una criatura propia es una entidad viva como cualquier otra: recibe los eventos spawn, damage y entity_death, tiene data y se maneja con lo de El mundo y los jugadores.

En 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)
        }
    }
}

Comportamientos: Behaviour.Swim, LookAround, Wander(speed), LookAtPlayers(range), MeleeAttack(speed), Panic(speed), Avoid(entity, distance, speed), Follow(item, speed). Objetivos: TargetRule.HurtBy y TargetRule.Nearest(id). El botín se añade con drop(item, count, chance), igual que en los bloques.

Tu propio modelo 3D

Añade model y la criatura se dibuja con tu modelo en lugar del del cuerpo. El cuerpo sigue dándole su tamaño, su movimiento y su comportamiento; el modelo solo cambia cómo se ve.

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

El modelo es models/entity/robot.json del mod y su textura es textures/entity/robot.png. En Kotlin: creature("robot") { body = "minecraft:pig"; model = "entity/robot"; texture = "entity/robot" }. El formato es el mismo en todas las versiones del juego.

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" } }

Los tamaños son en píxeles (16 es un bloque), con y apuntando hacia arriba desde los pies de la criatura.

  • Hueso: name, un parent opcional (se mueve con él), pivot (el punto alrededor del que gira) y cubes.
  • Cubo: origin (su esquina más baja), size y uv (dónde empieza su textura en la imagen). La disposición es la habitual de las cajas: un cubo de ancho w, alto h y fondo d usa un área de 2(w+d) por (d+h) píxeles.
  • Las animaciones van por hueso, como un nombre o una lista de nombres:
Animación Qué hace
look Gira hacia donde mira la criatura; para cabezas.
walk, walk_opposite Se balancea al andar; la segunda en fase opuesta, para la otra extremidad.
idle Un leve balanceo.
spin Gira sin parar.

Si un modelo no se puede dibujar, la criatura se ve como su cuerpo y el log dice por qué.

Tamaño

Una criatura es tan grande como su cuerpo. Cuando tu modelo es más pequeño o más grande, pon size: [width, height] en bloques para que aquello con lo que colisiona y el lugar donde se le hace clic coincidan con lo que ven los jugadores:

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

En Kotlin: size = 1.4 to 1.1.

Animaciones por fotogramas clave

Para movimientos más allá de las animaciones incluidas, añade una sección clips junto a bones y 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] } } } }
  } }
  • Cada clip tiene una length (duración) en segundos y, por hueso, hasta tres canales: rotation en grados, position en píxeles (y hacia arriba) y scale (un número o [x, y, z]).
  • Las claves son tiempos en segundos. Los valores se mezclan en línea recta entre claves; añade "interpolation": "smooth" a un clip y pasará por sus claves en una curva. Un canal también puede ser un solo valor para todo el clip.
Clip Cuándo se reproduce
walk Se repite mientras camina, al compás de sus patas.
idle Se repite mientras está quieta.
attack Se reproduce cuando golpea.
Cualquier otro nombre Se reproduce cuando el código lo pide: c.play_animation("roar"). Los jugadores cercanos que tengan Crafting lo ven.
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] } } } }

Un clip iniciado desde el código se reproduce sobre walk, idle y attack en vez de reemplazarlos, y se suman: una criatura puede rugir mientras camina.

Las crías de una criatura con modelo propio se dibujan a la mitad de tamaño.

Texturas

Sin modelo, la textura tiene la misma forma que la del cuerpo elegido (un zombi, un lobo…), así que puedes partir de una plantilla de ese cuerpo y pintarla. Va dentro del mod, como las de ítems y bloques:

Mod Dónde
CraftScript (carpeta) mods/ghostmod/textures/entity/ghost.png
Kotlin o Java src/main/resources/assets/ghostmod/textures/entity/ghost.png