API
Существа
Собственное существо использует тело игрового существа (его модель, анимации, размер и способ передвижения) с твоей текстурой поверх, твоими характеристиками, твоим поведением и твоей добычей. Оно объявляется как и остальной контент: списком свойств.
Объяви существо
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".
Призови его
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, и существо будет появляться в мире само, как существа самой игры.
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.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 сужает сущность до существа. Это проверяется при выполнении:
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 как любое другое, так что его место — его приоритет.
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)
Животные: разведение, приручение, езда и полёт
Несколько свойств и поведений превращают существо в животное. Это размножается с яблоками, приручается костью и позволяет ездить на себе:
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().
Собственные снаряды
Помимо снарядов игры, мод может объявлять свои. У него свой урон, своя гравитация и своё действие при попадании:
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.projectile("frost_bolt") {
damage = 4.0
gravity = false
onHit = { e -> /* … */ }
}Дальние атаки и снаряды
Поведение ranged_attack заставляет существо стрелять. Оно приближается, пока цель не окажется в зоне досягаемости и в поле зрения, и стреляет.
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:
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
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, и существо рисуется твоей моделью вместо модели тела. Тело по-прежнему задаёт его размер, движение и поведение; модель меняет только облик.
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" }. Формат одинаков во всех версиях игры.
{ "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 |
Постоянно вращается. |
"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] в блоках, чтобы то, с чем оно сталкивается, и то, куда по нему кликают, совпадало с тем, что видят игроки:
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:
{ "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 |