API

Существа

Собственное существо использует тело игрового существа (его модель, анимации, размер и способ передвижения) с твоей текстурой поверх, твоими характеристиками, твоим поведением и твоей добычей. Оно объявляется как и остальной контент: списком свойств.

Объяви существо

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] } ]
}
Свойство Значения По умолчанию
body id игрового существа: minecraft:zombie, minecraft:wolf, minecraft:cow… обязательно
texture путь к текстуре, которая накладывается на модель тела: entity/ghost — это textures/entity/ghost.png мода облик тела
model собственная 3D-модель: entity/robot — это models/entity/robot.json мода (ниже) модель тела
size [width, height] в блоках: с чем оно сталкивается и куда по нему можно кликнуть (ниже) как у тела
health очки здоровья как у тела
speed скорость ходьбы (зомби движется со скоростью 0.23, волк — 0.3) как у тела
attack урон в ближнем бою как у тела
follow_range на скольких блоках оно замечает свои цели как у тела
behaviour список поведений, от самого важного к наименее важному (ниже) поведение тела
targets кого оно атакует, в порядке приоритета (ниже) никого
loot [{ item, count, chance }, …], как у блоков: каждый предмет разыгрывается отдельно ничего
spawns биомы, где оно появляется само, с весом и размером группы (ниже) не появляется само
breed_item предмет, от которого два существа размножаются (ниже) не размножается
tame_item предмет, который его приручает (ниже) нельзя приручить
rideable true позволяет игрокам ездить на нём (ниже) false

Поведения

Список — это приоритет: когда два поведения хотят действовать одновременно, побеждает то, что раньше. Каждое записывается именем, как { name: number } для основной настройки или как словарь со всеми настройками.

Поведение Что делает Настройки
"swim" Держится на плаву в воде.
"look_around" Оглядывается, когда ничем не занято.
"wander", { wander: 0.8 } Бродит вокруг. speed, по умолчанию 1
"look_at_players", { look_at_players: 10 } Смотрит на игроков рядом. range в блоках, по умолчанию 8
"melee_attack", { melee_attack: 1.2 } Преследует цель и бьёт её. speed, по умолчанию 1
"panic", { panic: 1.5 } Убегает, когда получает урон. speed, по умолчанию 1.25
{ avoid: "minecraft:wolf", distance: 8, speed: 1.2 } Держится подальше от этого существа. distance в блоках, speed
{ follow: "minecraft:wheat", speed: 1 } Следует за тем, кто держит этот предмет. speed
{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 } Стреляет снарядом в свою цель (ниже). projectile, interval, range, speed, power, damage
"breed", { breed: 1 } Размножается с другим существом своего вида после кормления. speed
"follow_parent", { follow_parent: 1.1 } Детёныш держится рядом с родителем. speed
{ follow_owner: 1.2, distance: 8 } Прирученное существо следует за хозяином. speed, distance в блоках
{ fly_around: 1 } Летает вокруг; для летающих тел (попугай, пчела, аллэй). speed
{ can_start: …, tick: … } Твой собственный код (ниже). can_start, tick, keep_going, moves

Числа скорости умножают базовую скорость существа: при { wander: 0.8 } оно бродит чуть медленнее, чем ходит.

Цели

targets говорит, кого оно атакует, от высшего приоритета к низшему. "hurt_by" — тот, кто нанёс ему урон; id сущности — ближайшая сущность этого вида ("minecraft:player" для игроков, "minecraft:pig" для свиней). Чтобы атака произошла, существу нужно поведение, которое её выполняет, например "melee_attack".

Призови его

commands/haunt.craftCraftScript
command haunt requires "ghostmod.haunt" describe "Призвать призрака там, где ты стоишь" {
    if player == null { return "Только для игроков" }
    world().spawn("ghostmod:ghost", player.location)
    return "&7Появляется призрак…"
}

Id принадлежит твоему моду: ghostmod:ghost. Игровая команда /summon ghostmod:ghost тоже работает.

Появление само по себе

Добавь spawns, и существо будет появляться в мире само, как существа самой игры.

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

Каждая запись содержит biomes, где оно появляется (id или #тег для целой группы биомов), weight (вес: как часто, по сравнению с другими существами этого биома; у коровы 8, у зомби 95) и диапазон размера группы group.

Действуют правила появления его тела: с телом зомби оно появляется в темноте, с телом коровы — на траве при свете. Это работает и при генерации мира, и позже.

Яйцо призыва. Каждое существо получает также яйцо призыва, его id плюс _spawn_egg (ghost_spawn_egg), во вкладке творческого режима с яйцами призыва. Его текстура — textures/item/ghost_spawn_egg.png мода, если она есть; иначе оно выглядит как яйцо его тела.

На Kotlin:

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

Управление существом из кода

Существа, которые ты объявляешь, и любой другой моб — это Creature. Из события, команды или таймера им можно отдавать приказы:

Член Что делает
target Кого оно атакует. Можно читать и задавать.
moveTo(location, speed), move_to Идёт в указанную точку. Скорость умножает его обычную скорость.
stop() Перестаёт идти.
isMoving, is_moving Идёт ли оно куда-то.
lookAt(location), look_at Поворачивается, чтобы посмотреть на точку.
canSee(entity), can_see Есть ли у него прямая видимость до этой сущности.
isBaby Детёныш ли это.
ownerId Id его хозяина, после приручения.
tame(player) Делает этого игрока его хозяином.
isSitting Сидит ли оно.
playAnimation(name), play_animation Проигрывает одну из анимаций его модели (см. ниже).

У любой сущности есть velocity — Vector в блоках за тик. Прочитай или задай его, чтобы толкнуть сущность: entity.velocity = vector(0, 0.6, 0) подбрасывает её вверх.

В CraftScript типизированный let сужает сущность до существа. Это проверяется при выполнении:

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

Собственные поведения

Если готовых не хватает, напиши своё со своим кодом. Оно идёт в список behaviour как любое другое, так что его место — его приоритет.

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: запрашивается раз в несколько тиков; верни, должно ли оно начаться.
  • tick: выполняется каждый тик, пока поведение активно.
  • keep_going: говорит, продолжать ли. Если не указан, то же самое, что can_start.
  • moves: по умолчанию true. Поведение с движением не работает вместе с другим таким; выигрывает то, что выше в списке. Ставь false для тех, что только смотрят или реагируют.

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

Животные: разведение, приручение, езда и полёт

Несколько свойств и поведений превращают существо в животное. Это размножается с яблоками, приручается костью и позволяет ездить на себе:

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_item плюс поведение "breed": накормленные этим предметом, два существа одного вида приносят детёныша этого вида. Нужно тело животного (корова, волк, курица…). Детёныши используют "follow_parent", чтобы держаться рядом с родителями.
  • Приручение. tame_item: каждая попытка удаётся раз из трёх. Хозяин запоминается, а прирученное существо никогда не выбирает хозяина целью. Присядь и используй пустой рукой, чтобы оно село или встало. { follow_owner: 1.2, distance: 8 } заставляет его следовать за хозяином.
  • Езда. rideable: true: используй пустой рукой, чтобы забраться (после приручения — только хозяин). Управляют клавишами движения и мышью; присядь, чтобы слезть.
  • Полёт. { fly_around: 1 } для летающих тел (попугай, пчела, аллэй). Существо с rideable и летающим телом набирает высоту, когда всадник смотрит вверх, и снижается, когда он смотрит вниз.

На Kotlin: breedItem = …, tameItem = …, rideable = true и поведения Behaviour.Breed(), Behaviour.FollowParent(), Behaviour.FollowOwner(speed, distance) и Behaviour.FlyAround().

Собственные снаряды

Помимо снарядов игры, мод может объявлять свои. У него свой урон, своя гравитация и своё действие при попадании:

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: урон тому, во что он попадает.
  • gravity: false заставляет его лететь по прямой.
  • on_hit: fun(e), вызывается при попадании, с тем же e, что и у события hit: e.projectile, e.shooter и e.entity или e.block.
  • texture: снаряд летит с видом своего предмета, поэтому по умолчанию берёт textures/item/<id>.png; укажите texture, чтобы взять другую.

Используйте его как любой другой снаряд: world.shoot("mymod:frost_bolt", from, velocity, shooter) или, в существе, { ranged_attack: "mymod:frost_bolt" }. На Kotlin:

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

Дальние атаки и снаряды

Поведение ranged_attack заставляет существо стрелять. Оно приближается, пока цель не окажется в зоне досягаемости и в поле зрения, и стреляет.

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
    }
}
Параметр Значение
ranged_attack ranged_attack: снаряд (minecraft:arrow, minecraft:snowball, minecraft:small_fireball, minecraft:wind_charge…).
interval interval: тиков между выстрелами.
range range: расстояние в блоках, с которого оно стреляет.
speed speed: скорость ходьбы при приближении.
power power: как быстро летит снаряд.
damage damage: дополнительный урон при попадании.

Чтобы стрелять из кода, существами или способностью игрока, используй world().shoot(projectile, from, velocity, shooter, damage).

На 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)

Событие hit (ProjectileHitEvent) срабатывает, когда снаряд во что-то попадает. В нём есть e.projectile, e.shooter и e.entity или e.block, в зависимости от того, во что он попал. Отмени его, и снаряд пролетит насквозь. См. События.

Полезно знать

  • У каждого тела свои ограничения. У некоторых игровых существ нет какой-то характеристики: у коровы нет урона атаки. Если ты её задашь, она будет проигнорирована, а Crafting выведет предупреждение в журнал.
  • Тела, которые не ходят. Тела, которые не умеют сами прокладывать путь, игнорируют поведения передвижения (бродить, преследовать, паниковать, следовать, избегать); остальные работают.
  • Без behaviour существо ведёт себя как тело, которое оно использует. Как только ты пишешь список, оно делает только то, что в нём есть.
  • Модель меняет только облик. Движение и поведение всегда берутся от тела, как и размер, если вы не задали size.
  • Собственное существо — такая же живая сущность, как любая другая: оно получает события spawn, damage и entity_death, у него есть data, и ты управляешь им тем, что описано на странице Мир и игроки.

На 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)
        }
    }
}

Поведения: Behaviour.Swim, LookAround, Wander(speed), LookAtPlayers(range), MeleeAttack(speed), Panic(speed), Avoid(entity, distance, speed), Follow(item, speed). Цели: TargetRule.HurtBy и TargetRule.Nearest(id). Добыча добавляется через drop(item, count, chance), как у блоков.

Собственная 3D-модель

Добавь model, и существо рисуется твоей моделью вместо модели тела. Тело по-прежнему задаёт его размер, движение и поведение; модель меняет только облик.

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

Модель — это models/entity/robot.json мода, а её текстура — textures/entity/robot.png. На Kotlin: creature("robot") { body = "minecraft:pig"; model = "entity/robot"; texture = "entity/robot" }. Формат одинаков во всех версиях игры.

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

Размеры задаются в пикселях (16 — это один блок), ось y направлена вверх от ног существа.

  • Кость: name, необязательный parent (она движется вместе с ним), pivot (точка, вокруг которой она поворачивается) и cubes.
  • Куб: origin (его нижний угол), size и uv (где в изображении начинается его текстура). Развёртка обычная для коробки: куб шириной w, высотой h и глубиной d занимает область 2(w+d) на (d+h) пикселей.
  • Анимации задаются для каждой кости, именем или списком имён:
Анимация Что делает
look Поворачивает туда, куда смотрит существо; для голов.
walk, walk_opposite Качается при ходьбе; вторая — в противофазе, для другой конечности.
idle Лёгкое покачивание.
spin Постоянно вращается.
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] } } } }

Клип, запущенный из кода, проигрывается поверх walk, idle и attack, а не вместо них, и они складываются: существо может рычать на ходу.

Если модель не удаётся нарисовать, существо выглядит как его тело, а журнал объясняет почему.

Размер

Существо такого же размера, как его тело. Если ваша модель меньше или больше, задайте size: [width, height] в блоках, чтобы то, с чем оно сталкивается, и то, куда по нему кликают, совпадало с тем, что видят игроки:

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

На Kotlin: size = 1.4 to 1.1.

Анимации по ключевым кадрам

Для движений сверх готовых анимаций добавь раздел clips рядом с bones и 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] } } } }
  } }
  • У каждого клипа есть length (длина) в секундах и, для каждой кости, до трёх каналов: rotation в градусах, position в пикселях (y вверх) и scale (число или [x, y, z]).
  • Ключи — это моменты времени в секундах. Между ключами значения плавно меняются по прямой; добавьте клипу "interpolation": "smooth", и он будет проходить через свои ключи по кривой. Канал также может быть одним значением на весь клип.
Клип Когда играет
walk Повторяется, пока оно идёт, в такт ногам.
idle Повторяется, пока оно стоит.
attack Играет в момент удара.
Любое другое имя Играет, когда код попросит: c.play_animation("roar"). Её видят ближайшие игроки с Crafting.

Детёныши существа со своей моделью рисуются вполовину меньше.

Текстуры

Без модели развёртка текстуры такая же, как у выбранного тела (зомби, волка…), поэтому можно взять шаблон этого тела и рисовать поверх. Она лежит внутри мода, как текстуры предметов и блоков:

Мод Где
CraftScript (папка) mods/ghostmod/textures/entity/ghost.png
Kotlin или Java src/main/resources/assets/ghostmod/textures/entity/ghost.png