- Чтобы добавлять новые глобальные моды взаимодействующих с сервером (например новая персонализация - граффити)
- Для упращения работы с модами, вынесенные утилиты вам помогут не писать лишний код
- Автоматическое обновление базового пакета модов (стандартный пакет, квесты, лутбокс и прочее)
- Единый стиль и оформление режимов
repositories {
mavenCentral()
maven {
url 'https://repo.c7x.dev/repository/maven-public/'
credentials {
username System.getenv("CRI_REPO_LOGIN")
password System.getenv("CRI_REPO_PASSWORD")
}
}
}
dependencies {
implementation 'me.func:visual-driver:3.2.9.RELEASE'
// ссылка на nexus https://repo.c7x.ru/#browse/browse:maven-releases:me%2Ffunc%2Fvisual-driver
}STANDARD - стандартный набор модов (by @fiwka
, @func, @delfikpro
, @sworroo)
DIALOG - кит с квестовыми диалогами (by @sworroo
, @func)
LOOTBOX - набор для подключения лутбоксов (by @delfikpro
, @func)
MULTICHAT - набор для мультичата (by @zabelov
, @zenk)
GRAFFITI - кит с клиентом к сервису граффити и подключением мода (by @func)
EXPERIMENTAL - экспериментальный набор для тестирования новых модов (by @func)
NPC - модуль для работы с NPC (by @func)
BATTLEPASS - модуль для работы с BattlePass (by @akamex
, @func)
HEALTH_BAR - модуль добавляющий полоску здоровья (by @delfikpro)
DEBUG - модуль для удобной разработки модов (by @func
, @fiwka)
При старте плагина напишите Anime.include(Kit.STANDARD), так вы подключите стандартный набор модов, если вам нужны
другие модули, например Kit.LOOTBOX, Kit.DIALOG, Kit.STANDARD (другие будут добавлены позже), то укажите их через
запятую: Anime.include(Kit.LOOTBOX, Kit.DIALOG, Kit.STANDARD)
Моды берутся по вшитой ссылке на cristalix storage, если вы хотите заменить ссылку на наборы модов,
используйте env MOD_STORAGE_URL, ссылка должна оканчиваться на /, не рекомендуется.
Если вам не нужны граффити или нужно ограничить доступные места -
используйте Anime.modifyGraffitiPlaceCondition(canPlace: Predicate<Location>)
// Вариант 1
ModLoader.loadFromWeb("https://storage.c7x.ru/func/cristalix-dialogs.jar") // Загрузка мода в стандартную папку MOD_LOCAL_DIR_NAME
ModLoader.onJoining("cristalix-dialogs") // Загрузка мода в плагин, отправка игрокам мода при входе
// Вариант 2
ModLoader.onJoining(
"mod-bundle",
"cristalix-dialogs"
) // Пойдет искать загруженные моды, если кто-то не загружен, загрузит его из стандартной папки "anime", затем моды будут отправлены игрокам при входах на сервер
// Вариант 3
ModLoader.load("mods/mod-bundle.jar") // Загрузит мод по пути
ModLoader.send("mod-bundle.jar", player) // Отправит мод игроку
// Вариант 4
ModLoader.loadAll("mods") // Загрузит все моды из папки mods
ModLoader.send("mod-bundle.jar", player) // Отправит мод игрокуdownload(fileUrl: String, saveDir: String) скачивание файла по ссылке, в указанную папку (если файл уже скачан - он
перезапишется), пример download("http://cristalix.ru/cristalix-standard-mod.jar", "mods")
loadFromWeb(fileUrl: String, saveDir: String = MOD_LOCAL_DIR_NAME) загрузка файла в стандартную папку anime по
адресу, затем прогрузка мода на уровнь плагина
loadManyFromWeb(vararg fileUrls: String) то же самое, но можно указывать адреса через запятую
load(filePath: String) прогрузка мода указав путь к файлу (не web), мод сохраняется по ключу имени файла,
например mod-bundle.jar
loadAll(dirPath: String) загрузить все моды из папки по путю к папке
send(modName: String, player: Player) отправка мода игроку, пример имени mod-bundle.jar
manyToOne(player: Player) отправить все прогруженные моды игроку
oneToMany(modName: String) отправить один мод по имени всем игрокам
sendAll() отправить все прогруженные моды всем игрокам
// Пример 1 (Умный конструктор)
ModTransfer("Осталось %n% жизней!", 76666).send("func:health", player)
// Пример 2 (Билдер)
ModTransfer()
.item(ItemStack(AIR)) // Предмет, можно NMS, Bukkit
.nbt(NBTTagCompound()) // Любой тег
.string("Привет") // Строка
.json(object) // Любой объект, будет превращен в JSON строку
.byteBuffer("heyy".bytes()) // Массив byte
.double(43.5) // Прочие числа
.varInt(4)
.putDouble(5.6) // Методы c "put" для Java
.send("func:lol", player)
// Привет 3 (Переиспользование буффера)
val data = ModTransfer().integer(items.size)
.item(item.itemStack)
.string(item.title)
.string(item.rare.name)
players.forEach { player -> data.send("lootbox", player) } Простым языком: вы кидаете мод в папку, API это видит и перезагружает мод у игроков.
Данный режим позволяет мнгновенно обновлять мод на клиенте игрока, что очень удобно для быстрого тестировая. Обычные
этапы разработки: написание кода -> компиляция -> заливка мода в папку сервера -> перезагрузка сервера -> вход на сервер
-> загрузка мода, всего 6 этопов. С данным режимом: написание кода -> компиляция -> заливка мода в папку сервера ->
смотрим, всего 4 этапа. Так же можно автоматически загруждать мод в папку прямо из среды разработки, но это уже задача
вашей среды.
Чтобы включить режим быстрого тестирования, допишите в Anime#include кит Kit.DEBUG. По умолчанию папка mods
является хранилищем тестовых модов, чтобы сменить стандартную папку - измените переменную среды MOD_TEST_PATH.
Новый скорборд для тайконов и симуляторов! ВАЖНО! Группу нужно создать только один раз, не нужно ее делать на каждого игрока
Пример создания на Kotlin
// Где-то на уровне класса
private val group = TokenGroup(
Token.builder()
.title("Деньги")
.content { player -> Emoji.DOLLAR + " §a" + convert(user(player).vault[user(player).locationType!!.moneyType]!!) }
.build(),
Token.builder()
.title("Кристалики")
.content { player -> Emoji.DONATE + " §b" + convert(app.getUser(player)!!.donateCoins.toDouble()) }
.build(),
Token.builder()
.title("Склад")
.content { player -> Emoji.MINING + " §6" + convert(getStorage(player)) }
.build(),
)
// При входе игрока, через пару тиков
group.subscribe(player)
// Удалить токены из группы
group.removeTokens(group.tokens[0].uuid) Сделали скорборд? Подписали на него игроков? - Ну все, теперь работает
Создание
val schema = ScoreBoard.builder()
.key("lobby") // ключевое слово скорборда
.header("Бедроковая коробка") // указываем верхушку
.empty() // пустая строка
.line("Привет") // неизменяемая строка
.line("Как дела", "433") // неизменяемая строка и значение
.empty() // пустая строка
.dynamic("Коины", player -> System.currentTimeMillis()) // слева "Коины", справа изменяемое значение
.build() // получаем схему
Показ игрокам (если делаете при входе, не забудьте задержку в 1 тик)
ScoreBoard.subscribe("lobby", player) // по ключевому слову
schema.show(player) // по схеме (переменная объявлена в примере выше)
На данный момент реализован только реактивный прогрессбар (меняете поля на сервере - меняются на клиентах), работает на экране игрока и в мире (если указать offsetZ != 0.0).
Создание на языка java
ReactiveProgress.builder() // примеры того, что можно настроить
.position(Position.BOTTOM) // указать стартовую позицию снизу
.offsetY(32) // сместить вверх на 32 "пикселя"
.hideOnTab(false) // не скрывать при нажатии таба
.color(GlowColor.GREEN) // поставить зеленый цвет, можно поставить любой RGB, например через Tricolor
.build();Чтобы показать его группе игроков и подписать их на обновление, используйте ReactiveProgress#send(vararg players: Player)
Отписать игроков от обновления - ReactiveProgress#unsubscribe(vararg players: Player)
Стереть прогресс у игроков и отписать от обновления - ReactiveProgress#delete(vararg players: Player)
Чтобы прогресс бар появился в мире, используйте поля offset, как координаты или в билдере вызовите location(origin: Location)
Как реактивно менять данные прогрессбара?
- Уровень прогресса, вызовите
ReactiveProgress#setProgress(value: Double)(число от 0 до 1) - Строку над прогрессом, вызовите
ReactiveProgress#setTest(test: String)
Selection
Choicer
- Чтобы загрузить свою текстуру, используйте
Anime#loadTexturesилиAnime#loadTexture(возвращает texture). - Если вы хотетите сменить валюту, то нужно изменить поле
vaultвSelectionна символ валюты или путь к текстуре - Если нужно закрыть меню, используйте
Anime#close, которое закрывает любое окно игрока, кроме меню достижений и настроек.
Пример для языка kotlin
val menu = selection {
title = "Прокачки" // Название слева сверху
money = "Ваш баланс 999" // Баланс справа сверху, если не указать - монетка не покажется
hint = "Взять" // При наведении на предмет, будет отображаться этот текст
rows = 3 // Количество строчек
columns = 4 // Количество колонок
buttons( // Указание кнопок, если кнопок будет больше чем вмещается, меню станет многостраничным
button {
texture = "minecraft:textures/items/apple.png" // Текстура namespace:path
price = 88L // Цена предмета, если не указать - монета не покажется
title = "Куриные\nкрылья" // Название предмета (есть поддержка \n)
description = "+10 силы" // Комментарий предмета (есть поддержка \n)
onClick { player, index, button -> // Обработка нажатия по кнопке
player.sendMessage("Button id: $index, button $button")
}
},
button {
texture = Sprites.DUO.path() // Текстура с двойным режимом
price = 88L // Цена предмета, если не указать - монета не покажется
title = "Куриные\nкрылья" // Название предмета (есть поддержка \n)
},
button {
item = ItemStack(Material.HEAD) // Добавление иконки кнопки по предмету
price = 99
hint = "Продать" // Надпись при наведении на кнопку
title = "Картошка"
description = "+гниль"
hover = "Привет" // Всплывающий текст при наведении
}.sale(60) // Указать скидку 60%
)
}
menu.open(player) // Открываем меню игроку, можно сохранять меню в переменную и показывать много раз
// Кнопки можно менять реактивно после открытия меню, ставите title и она меняется у игроков Пример для языка java
Selection menu = new Selection(
"Прокачки",
"Баланс 999",
"Купить",
4, // Количество строчек
3, // Количество колонок
new ReactiveButton()
.texture("minecraft:textures/items/apple.png")
.price(999)
.title("Название")
.description("описание")
.hover("Текст при наведении")
.onClick((player, index, button) -> {
player.sendMessage("Button id: $index, button $button");
}),
new ReactiveButton().material(Material.HEAD).sale(30) // Иконка по предмету/материалу, скидка 30%
);
menu.open(player);Пример для языка kotlin
var menu = Reconnect { player -> player.sendMessage("Возвращаем вас в игру!") } // Первый вариант конструктора
menu = Reconnect(90) { player -> player.sendMessage("Возвращаем вас в игру!") } // Теперь указываем сколько секунд осталось
menu = Reconnect("Продать почку", 90, "Продать") { player -> player.sendMessage("Возвращаем вас в игру!") } // Теперь указываем все что только можно
menu.open(player) // Открываем меню игрокуПозволяет удостовериться, в корректности мотивов игрока. Поддерживает несколько строк.
Пример для языка kotlin
val menu = Confirmation("Купить Уганду за", "602 рубля", "Или все же нет?") { player -> // Через запятую указываем строки сообщения меню
player.sendMessage("Успешная покупка!") // Пишем что делать при подтверждении игроком
}
menu.open(player) // Открываем меню игрокуПример для языка java
Confirmation menu = new Confirmation(Arrays.asList("Купить Уганду за", "602 рубля", "Или все же нет?"), player -> { // Через запятую указываем строки сообщения меню
player.sendMessage("Успешная покупка!") // Пишем что делать при подтверждении игроком
});
menu.open(player); // Открываем меню игрокуПанели над хотбаром, их можно указывать сколько угодно, вся ширина делится в таком же отношении, в котором делится текст ко всему тексту. Можно реактивно менять текст/прогресс/цвет, стратегию реализуйте сами хук в сеттер/по таймингу.
val moneyPanel = ReactivePanel.builder()
.color(GlowColor.GOLD)
.progress(0.5)
.text(Emoji.COIN + " " + stat?.money)
.build()
moneyPanel.send(player)Что представляет из себя батлпасс?
Это приобретаемый набор заданий, за выполнение которых даются внутриигровые вещи.
val battlePass = BattlePass.new(399) {
pages = arrayListOf(
BattlePassPageAdvanced(
// Первая страница баттлпасса
300, // Опыт для прохождения уровня
10, // Цена скипа уровня
listOf(
// Список обычных предметов
NameTag.TAG1.getIcon(),
ArrowParticle.SLIME.getIcon(),
ArrowParticle.WATER_DROP.getIcon(),
StepParticle.SLIME.getIcon(),
Mask.HOUSTON.getIcon(),
KillMessage.GLOBAL.getIcon(),
MoneyKit.SMALL.getIcon(),
ArrowParticle.FALLING_DUST.getIcon(),
ArrowParticle.SPELL_INSTANT.getIcon(),
Mask.JASON.getIcon(),
),
listOf(
// Список премиум предметов
NameTag.TAG3.getIcon(),
Mask.TRADEGY.getIcon(),
Mask.HORROR.getIcon(),
MoneyKit.SMALL.getIcon(),
NameTag.TAG28.getIcon(),
Mask.COMEDY_MASK.getIcon(),
KillMessage.END.getIcon(),
ArrowParticle.REDSTONE.getIcon(),
StepParticle.REDSTONE.getIcon(),
Corpse.G1.getIcon(),
),
),
BattlePassPageAdvanced(
// Вторая страница баттлпасса
600,
20,
listOf(
KillMessage.DEAD.getIcon(),
MoneyKit.SMALL.getIcon(),
Mask.SCREAM.getIcon(),
NameTag.TAG10.getIcon(),
StepParticle.REDSTONE.getIcon(),
KillMessage.ROOM.getIcon(),
Mask.DALLAS.getIcon(),
NameTag.TAG16.getIcon(),
Mask.CREWMATE_LIME.getIcon(),
MoneyKit.NORMAL.getIcon()
),
listOf(
MoneyKit.SMALL.getIcon(),
KillMessage.SLEEP.getIcon(),
Mask.CREWMATE_WHITE.getIcon(),
NameTag.TAG17.getIcon(),
Mask.JASON.getIcon(),
ArrowParticle.VILLAGER_ANGRY.getIcon(),
NameTag.TAG20.getIcon(),
MoneyKit.NORMAL.getIcon(),
Mask.CREWMATE_PURPLE.getIcon(),
StepParticle.VILLAGER_ANGRY.getIcon()
),
)
)
sale(50.0) // Скидка 50%
facade.tags.add("Выполняйте квесты - получайте призы!")
facade.tags.add("BattlePass завершится в 01.04.2022")
}
BattlePass.send(player, battlePass)
BattlePass.show(player, battlePass, BattlePassUserData(100, false))Редкость предмета берется из NBTTag предмета rare (DropRare), а статус собранности награды taken (Boolean).
Можно указывать награду на каждый уровень, требуемый опыт.
Что представляет из себя баннер?
Это голограмма, рисунок в мире, маркер и прочее что является прямоугольником в мире.
Banners.new {
motionType = MotionType.CONSTANT // тип движения
watchingOnPlayer = false // смотрит ли баннер вечно на игрока
motionSettings = hashMapOf(
"yaw" to 0.0,
"pitch" to 0.0
) // магия
content = "" // текст на прямоугольнике
x = 0.0 // координата x
y = 0.0 // координата y
z = 0.0 // координата z
height = 100 // высота
weight = 100 // ширина
texture =
"" // текстура, например minecraft:apple (не забывайте что текстуру можно загружать через мод по web-ссылке)
red = 0 // красный цвет прямоугольника
green = 0 // зеленый цвет прямоугольника
blue = 0 // синий цвет прямоугольника
opacity = 0.62 // степерь прозрачности, от 0.0 до 1.0
}Что за типы движения?
STEP_BY_TARGET - баннер следует за сущностью
PERIODIC - баннер двигается по периодичной траектории
CONSTANT - баннер не двигается
Как создать Banner?
// Kotlin пример
Banners.new { // Баннер добавится в список баннеров и когда игроки будут заходить, они его увидят
// Элементарная заменя характеристик (указана лишь часть)
x = 1.0
y = 100.0
z = 1111000.0
texture = "minecraft:apple"
red = 255
opacity = 1.0
content = "Этот текст -\nмногострочный"
height = 120
weight = 200
// Продвинутые методы для замены свойств
location(player.location) // Указание места баннера через локацию
eyeLocation(player.bedSpawnLocation) // Указание поворота через точку, на которую нужно смотреть
target(player, 0.0, 0.0, 2.1) // Теперь баннер следует за игроком, баннер выше игрока на 2.1 блока
shineBlocks(false) // Чтобы баннер не просвечивался через стены
}// Java пример
Banner banner = Banner.S.builder() // Создание баннера
.x(location.x)
.y(location.y+50)
.z(location.z)
.opacity(0.0) // Прозрачность, от 0.0 до 1.0
.height(50) // Высота
.weight(50) // Ширина
.content("§aЯ голограмма","§eОчень красивая","§cИ приятная глазу") // Многострочный текст
.watchingOnPlayer(true) // Указание, чтобы баннер всегда смотрел на игрока
.build();
Banners.add(banner); // Добавление в список баннеров, которые будут показаны игроку при входе на серверМетоды утилиты Banners:
Banners.new(data: Banner.() -> Unit): Banner kotlin версия конструктора для баннера (пример выше)
Banners.new(banner: Banner): Banner java версия конструктора для баннера (пример выше)
Banners.content(player: Player, uuid: UUID, content: String) отправка нового текста игроку на баннер
Banners.content(player: Player, banner: Banner, content: String) тоже самое, но через объект баннера
Banners.show(player: Player, vararg uuid: UUID) показать игроку все указанные баннеры по uuid (баннеры уже есть в
списке глобальных)
Banners.show(player: Player, vararg banner: Banner) показать игроку все баннеры (они могут не быть в списке баннеров и
созданы через конструктор Banner)
Banners.remove(uuid: UUID) удалить баннер из списка (не удаляет баннер игрокам)
Banners.hide(player: Player, vararg uuid: UUID) удалить игроку все указанные баннер(ы)
Banners.hide(player: Player, vararg banner: Banner) тоже самое, но через объекты класса Banner
Список доступных индикаторов (enum Indicators):
HEALTH индикатор здоровья над инвентарем,
EXP индикатор уровня,
HUNGERиндикатор голода игрока,
ARMOR индикатор брони,
VEHICLE индикатор здоровья управляемого моба (свинья, лощадь...),
TAB список игроков при нажатии TAB
HOT_BAR 9 слотов инвенторя игрока снизу экрана
AIR_BAR воздух под водой
POTIONS активные эффекты зелий
HAND правая рука
NAME_TEMPLATE ники игроков
Методы взаимодействия с клиентом:
Anime.hideIndicator(player: Player, vararg indicator: Indicators) скрывает игроку указанные через запятую
индикаторы
Anime.showIndicator(player: Player, vararg indicator: Indicators) показывает игроку указанные через запятую
индикаторы
Методы взаимодействия с клиентом:
Anime.sphere(to: Player, uuid: UUID, location: Location, color: Color, radius: Double) создать сферу по указанным
координатам с указанным радиусом
Anime.sphere(to: Player, uuid: UUID, location: Location, color: Color, sX: Double, sY: Double, sZ: Double) создать
сферу по указанным координатам с указанными размерами
Anime.teleportSphereTo(to: Player, uuid: UUID, location: Location) телепортировать сферу
Anime.removeSphere(to: Player, uuid: UUID) удалить сферу
Anime.moveSphereTo(to: Player, uuid: UUID, location: Location, time: Double) плавно переместить сферу по указанным
координатам за указанное количество секунд
val chat = ModChat(
UUID.randomUUID(), //Id чата
"Системный чат", //Название
"С" //Символ на кнопке (можно несколько символов)
)
MultiChat.createKey("system", chat) //Делаем удобный ключ для использования
//Делаем обработку сообщений
MultiChat.registerHandler(chat) { player, message ->
player.sendMessage("Ты отправил в этот чат: $message")
}
MultiChat.sendChats(player, "system") //Отправляем игроку чат
MultiChat.sendMessage(player, "system", "Ты крутой сынок!") //Отправляем игроку сообщение
MultiChat.removeChats(player, "system") //Удаляем у игрока чатМетоды взаимодействия с клиентом:
MultiChat.createKey(id: String, chat: ModChat) создать ключ для удобного использования чата
MultiChat.removeKey(chat: ModChat) удалить ключ
MultiChat.removeKey(key: String) удалить ключ по ключу чата
MultiChat.removeKey(id: UUID) удалить ключ по id чата
MultiChat.registerHandler(id: UUID, consumer: BiConsumer<Player, String>) добавить обработчик для сообщений, которые приходят в чат по id чата
MultiChat.registerHandler(key: String, consumer: BiConsumer<Player, String>) добавить обработчик для сообщений, которые приходят в чат по ключу чата
MultiChat.sendChats(player: Player, vararg chats: ModChat) отправить игроку сообщение все указанные чаты
MultiChat.sendChats(player: Player, vararg chats: UUID) отправить игроку сообщение все указанные чаты по их id
MultiChat.sendChats(player: Player, vararg chats: String) отправить игроку сообщение все указанные чаты по их ключу
MultiChat.removeChats(player: Player, vararg chats: ModChat) удалить игроку все указанные чаты
MultiChat.removeChats(player: Player, vararg chats: UUID) удалить игроку все указанные чаты по их id
MultiChat.removeChats(player: Player, vararg chats: String) удалить игроку все указанные чаты по их ключу
MultiChat.sendMessage(player: Player, chat: ModChat, message: String) отправить игроку сообщение в чат
MultiChat.sendMessage(player: Player, chat: UUID, message: String) отправить игроку сообщение в чат по id чата
MultiChat.sendMessage(player: Player, chat: String, message: String) отправить игроку сообщение в чат по ключу чата
MultiChat.broadcast(players: Collection<Player>, chat: ModChat, message: String) отправить указанным игрокам сообщение в чат
MultiChat.broadcast(chat: ModChat, message: String) отправить всем игрокам на сервере сообщение в чат
MultiChat.broadcast(players: Collection<Player>, chat: UUID, message: String) отправить указанным игрокам сообщение в чат по id чата
MultiChat.broadcast(chat: UUID, message: String) отправить всем игрокам на сервере сообщение в чат по id чата
MultiChat.broadcast(players: Collection<Player>, chat: UUID, message: String) отправить указанным игрокам сообщение в чат по id чата
MultiChat.broadcast(chat: UUID, message: String) отправить всем игрокам на сервере сообщение в чат по id чата
MultiChat.broadcast(players: Collection<Player>, chat: String, message: String) отправить указанным игрокам сообщение в чат по ключу чата
MultiChat.broadcast(chat: String, message: String) отправить всем игрокам на сервере сообщение в чат по ключу чата
Цвета:
- Цвета состоят из трез целых чисел и одного дробного, красный [0 .. 255], синий [0 .. 255], зеленый [0 .. 255], прозрачность [0 .. 1.0]
- Вы можете еспользовать
enum GlowColorс готовыми частыми цветами или создать свойTricolor()
Методы взаимодействия с клиентом:
Glow.set(player: Player, red: Int, blue: Int, green: Int, alpha: Double) ставит игроку свечение
Glow.set(player: Player, color: RGB) то же самое, но через RGB
Glow.animate(player: Player, seconds: Double, red: Int, blue: Int, green: Int, alpha: Double) плавное появление и
скрытие свечения с указанием длительности этой анимации
Glow.animate(player: Player, seconds: Double, color: RGB, alpha: Double) то же самое, но через RGB
Glow.animate(player: Player, seconds: Double, red: Int, blue: Int, green: Int) то же самое, но без указания
прозрачности (без прозрачности)
Glow.animate(player: Player, seconds: Double, color: RGB) то же самое, но через RGB
Методы добавления места в глобальный список мест (не показывает игрокам!):
Glow.addPlace(red: Int, blue: Int, green: Int, x: Double, y: Double, z: Double, onJoin: (Player) -> Unit)
Glow.addPlace(red: Int, blue: Int, green: Int, x: Double, y: Double, z: Double)
Glow.addPlace(color: RGB, x: Double, y: Double, z: Double, onJoin: (Player) -> Unit)
Glow.addPlace(color: RGB, x: Double, y: Double, z: Double)
Glow.addPlace(place: GlowingPlace, onJoin: Consumer<Player>)
Glow.addPlace(place: GlowingPlace)
Методы показа мест игроку:
Glow.showPlace(player: Player, place: GlowingPlace) показать игроку место (место может быть не глобальным!)
Glow.showLoadedPlace(player: Player, uuid: UUID) показать игрку глобальное место по UUID
Glow.showAllPlaces(player: Player) показать игроку все глобальные места
Очистка мест:
Glow.removePlace(place: GlowingPlace, vararg players: Player) удаление места для указанных игроков и удаление из
списка глобальных
Glow.clearPlaces(vararg players: Player) очистить указанным игрокам места и удалить все места их глобального
списка
Показывает текущее состояние бустера игрока.
Пример для языка Kotlin:
val boosterBar = boosterBar {
segments = listOf()
title = ""
subtitle = ""
isShowBackground = false
progress = 1.0
}
boosterBar.open(player)Показывает текущее состояние бустера игрока.
Метод:
Anime.startBoosters(player: Player, vararg boosters: Booster) показать индикатор
Методы:
Anime.reload(player: Player, seconds: Int, text: String) простая перезарядка, с розовым цветом
Anime.reload(player: Player, seconds: Int, text: String, color: RGB, alpha: Double) с использованием
RGB
Anime.reload(player: Player, seconds: Int, text: String, red: Int, green: Int, blue: Int, alpha: Double) с любым
цветом в RGB
Методы:
Устаревший метод, который выводит сообщение только в правом нижнем углу:
Anime.bottomRightMessage(player: Player, text: String) поставить справа снизу информацию игроку (строка делится
по \n)
Anime.bottomRightMessage(player: Player, vararg text: String) тоже самое, но удобнее, так как можно указать строки
через запятую
Новый метод, который выводит в 4 разных угла:
Anime.overlayText(player: Player, position: Position, text: String) позволяет размещать текст в разные стороны экрана (строка делится
по \n)
Anime.overlayText(player: Player, position: Position, vararg text: String) тоже самое, но удобнее, так как можно указать строки
через запятую
Методы:
Npc.npc(init: NpcData.() -> Unit): NpcSmart kotlin вариант конструктора для создания NPC (только через него можно
сделать глобальных NPC, которых плагин отправляет игроку при заходе на сервер)
Npc.link(data: NpcData): NpcSmart java версия (делает тоже самое)
Npc.spawn(entityId: Int) показать всем игрокам заранее созданный NPC по entityId (оно есть в NpcData)
Npc.kill(entityId: Int) спрятать всем игрокам заранее созданный NPC по entityId (оно есть в NpcData)
Npc.hide(entityId: Int, player: Player) скрыть конкретному игроку NPC по entityId
Npc.show(entityId: Int, player: Player) показать конкретному игроку NPC по entityId
Npc.clear() удалить всем игрокам всех NPC (не чистит глобальный список NPC)
NpcSmart:
var data: NpcData, // данные на клиенте
var click: Consumer<PlayerUseUnknownEntityEvent>? = null, // обработчик нажатия на NPC
private var leftArm: ItemStack? = null, // предмет в левой руке
private var rightArm: ItemStack? = null, // предмет в правой руке
private var head: ItemStack? = null, // предмет на голове
private var chest: ItemStack? = null, // предмет на груди
private var legs: ItemStack? = null, // предмет на ножках
private var feet: ItemStack? = null, // предмет на стопахNpcSmart.slot(slot: EquipmentSlot, itemStack: ItemStack): NpcSmart метод обновляющий слот NPC для всех игроков
NpcSmart.slot(slot: EquipmentSlot, itemStack: ItemStack, player: Player): NpcSmart метод обновляющий слот NPC для
конкретного игрока
NpcSmart.kill(): NpcSmart скрыть всем игрокам этого NPC
NpcSmart.show(player: Player): NpcSmart показать игроку этого NPC
NpcSmart.hide(player: Player): NpcSmart скрыть игроку этого NPC
NpcSmart.update(player: Player): NpcSmart обновить игроку данные NPC (имя, координаты, поворот головы, положение тела
- сидеть, спать, шифтить)
NpcSmart.swingArm(mainHand: Boolean, player: Player): NpcSmartпошевелить рукой, указав главная ли рука и игрока-получателя
NpcSmart.spawn(player: Player): NpcSmartсоздать игроку NPC <--- Только тут игроку создается с нуля NPC
NpcData:
var id: Int = (Math.random() * Int.MAX_VALUE).toInt(), // entityId NPC
val uuid: UUID = UUID.randomUUID(), // UUID
var x: Double = 0.0,
var y: Double = 0.0,
var z: Double = 0.0,
var type: Int = 1000, // тип сущности 1000 - ИГРОК
var name: String? = null, // имя
var behaviour: NpcBehaviour = NpcBehaviour.NONE, // модель поведения NPC
var pitch: Float = 0f,
var yaw: Float = 0f,
var skinUrl: String? = null, // ссылка на скин
var skinDigest: String? = null, // ЕСЛИ ВЫ СТАВИТЕ ССЫЛКУ, ТО СТАВЬТЕ DIGEST (например uuid.toString())
var slimArms: Boolean = false, // тонкие руки
var sneaking: Boolean = false, // шифтит
var sleeping: Boolean = false, // спит
val sitting: Boolean = false, // сидит
val activationDistance: Int = -1 // -1 = без ограничения по блоком, любое другое число большее, чем 0 - радиус, в котором начинает смотреть на игрокаNpcData.onClick(init: Consumer<PlayerUseUnknownEntityEvent>) при клике на NPC обработать событие
NpcData.location(location: Location) указать координаты NPC через локацию
Модели поведения:
NONE - нет поведения
STARE_AT_PLAYER - следит за игроком
STARE_AND_LOOK_AROUND - смотрит на игрока
Пример на Kotlin:
npc {
x = 1337.0
y = 225.5
z = -102.3
behaviour = NpcBehaviour.STARE_AND_LOOK_AROUND
onClick {
it.player.sendMessage("Хай")
}
}.slot(EquipmentSlot.HAND, CraftItemStack.asNMSCopy(ItemStack(Material.DIAMOND_BLOCK))).spawn()Методы взаимодействия с клинтом:
Dialog.sendDialog(player: Player, dialog: Dialog) отправляет игру ветку диалогов
Dialog.openDialog(player: Player, dialogId: String) показывает игроку диалог по ID ветки
Dialog.dialog(player: Player, dialog: Dialog, openEntrypoint: String) отправляет и показывает игроку диалог по ID
ветки
Структура диалога:
Dialog состоит из набора точек входа Entrypoint, в одном хранятся Screen, это конкоетно что видит игрок - набор
строк и кнопок Button с действиями Action, например открытия нового окна. Набор Action вы можете увидить
в enum Actions
Пример кода:
new Dialog(new Entrypoint(
"id",
"название",
new Screen(а
"Первая видимая строка",
"Вторая видимая строка"
).buttons(
new Button("Имя кнопки").actions(
new Action (Actions.COMMAND).command("/next"),
new Action (Actions.CLOSE)
),
new Button ("Войти в очередь").actions(
Action("/hub"),
new Action (Actions.CLOSE)
)
)
))Показ окна наград:
DailyRewardMenu.builder()
.rewards(buttons...) // 7 кнопок с наградами
.taken(false) // награда еще не взята
.currentDay(1) // текучая награда - это первый день (начинается с 1)
.build()
.open(player); // открыть игрокуfunc:reward:click в этот канал прийдет callback, а именно - номер для (int), словить его можно с Anime#createReader, УЯЗВИМОСТЬ игрок может написать чит, и сам отправлять в данный канал инфу, проверяйте, может ли игрок получить награду!
Уведомления возникают у игрока справа сверху, можно отправлять несколько, уведомление содержит UUID отправителя, в нем
может быть максимум 2 кнопки. Данная возможность не зависит от модулей animation-api, мод предустановлен
через bungeecord всем игрокам (Исходники мода cristalix-socials не будут добавлены в данный репозиторий).
Для начала рассмотрим создание кнопок уведомлений:
Alert.button( // метод возвращает кнопку
"Принять", // сообщение на кнопке
"/ok", // команда при нажатии
GlowColor.GOLD, // цвет кнопки
false, // после нажатия удалится ли эта кнопка
true // после нажатия скрыть все уведомление
)Всего есть два подхода к отправке уведомлений игроку:
1. Генерация уведомления прямо на месте отправки
2. Создание шаблона уведомления, затем замена контента под ситуацию, отправка
Рассмотрим первый вариант:
Alert.send(
player = player, // кому отослать
text = string, // сообщение
millis = long, // милисекунд до скрытия уведомления (1000 = 1 секунда)
frontColor = glowColor, // внешний цвет
backGroundColor = glowColor, // цвет фона
chatMessage = string, // сообщение в чате
buttons: notificationButton... // кнопки (максимум две) (vararg)
)Рассмотрим второй вариант:
- Создание шаблона уведомления
Alert.put(
"hello", // ключ
NotificationData( // содержимое
null,
"notify",
"Привет %nick%,\nкак дела?",
toRGB(GlowColor.GREEN),
toRGB(GlowColor.GREEN),
3000,
listOf(
button("Принять", "/ok", GlowColor.GOLD) // создание кнопки
),
"Вам уведомление!"
)
)- Получение шаблона, замена текста, отправка:
Alert.find("hello") // найти шаблон по ключу
.replace("%nick", player.name) // заменить placeholder
.replace("как дела?", "как спалось?")
.send(player) // отправить игрокуМетод показа дропа с лутбокса:
Anime.openLootBox(player: Player, vararg items: LootDrop) открыть игроку лутбокс с указанием всего что выпало в виде
объектов LootDrop(сам предмет, название предмета, <НЕ ОБЯЗАТЕЛЬНО ПО УМАЛЧАНИЮ COMMON> редкость)
Редкости предметов:
enum class DropRare(val title: String, val color: String) {
COMMON("Обычный", "§a"),
RARE("Редкий", "§9"),
EPIC("Эпический", "§5"),
LEGENDARY("Легендарный", "§6"), ;
fun getColored(): String {
return "$color$title" // метод для получения окрашенного имени редкости
}
}Список экранов окончания игр (enum EndStatus):
WIN экран победы,
LOSE экран поражения,
DRAW экран ничьей,
Методы вызова экрана окончания игры:
Anime.showEnding(player: Player, endStatus: EndStatus, key: String, value: String) показывает экран окончания игроку,
с информацией. key - сообщения слева, value - сообщения справа. Используйте \n \n - для переноса строки.
Anime.showEnding(player: Player, endStatus: EndStatus, key: List<String>, value: List<String>) показывает экран
окончания игроку. key - список строк слева, value - список строк справа.
Пример использования:
Anime.showEnding(
player,
EndStatus.WIN,
listOf("Убийств:", "Смертей:"),
listOf("${user.kills}", "${user.deaths}")
)Указаниие более чем 5 полей приведёт к нечитабельности статистки!
!!! ВАЖНО !!!
Это частный случай Banners, класс помечен @Deprecated, новый инструмент способен делать намного больше, рекомендуем
испольовать его
Что такое маркер? Это случайный UUID генерируемый в конструкторе, координаты в текущем мире игрока x, y, z, размер
текстуры и путь к текстуре на клиенте (обычно через resource-pack, но можно загрузить игроку текстуру через метод в
разделе "Прочее"). Для упращения работы с текстурами, вынесено несколько базовых в enum MarkerSign:
FINE("textures/others/znak_v_3.png")
ERROR("textures/others/znak_v_2.png")
WARNING("textures/others/znak_v_1.png")
QUESTION_FINE("textures/others/z1.png")
QUESTION_ERROR("textures/others/z2.png")
QUESTION_WARNING("textures/others/z1.png")
ARROW_DOWN("mcpatcher/cit/others/badges/arrow_down.png")
ARROW_UP("mcpatcher/cit/others/badges/arrow_up.png")
ARROW_RIGHT("mcpatcher/cit/others/badges/arrow_right.png")
ARROW_LEFT("mcpatcher/cit/others/badges/arrow_left.png")Пример маркера: new Marker(225, 1, 5, 10, MarkerSign.ERROR)
Методы взаимодействия с клинтом:
Anime.marker(player: Player, marker: Marker) показать маркер игроку
Anime.markers(player: Player, vararg markers: Marker) показать игроку кучу маркеров
Anime.clearMarkers(player: Player) очистить все маркеры у игрока
Anime.removeMarker(player: Player, marker: Marker) удалить игроку маркер по маркеру
Anime.removeMarker(player: Player, uuid: UUID) удалить игроку маркер по UUID маркера
Anime.moveMarker(player: Player, marker: Marker) обновить нахождение маркера у игрока
Anime.moveMarker(player: Player, marker: Marker, seconds: Double) обновить нахождение маркера у игрока анимированно за
указанное время
Anime.moveMarker(player: Player, uuid: UUID, toX: Double, toY: Double, toZ: Double, seconds: Double) обновить
нахождение маркера у игрока анимированно за указанное время имея только UUID маркера
Anime.topMessage(player: Player, message: String) сообщение сверху (поддерживает цвета и многострочные сообщения)
Anime.title(player: Player, text: String) многострочный title, поддерживает все цвета, а так же \n
Anime.title(player: Player, vararg text: String) то же самое, но строчк можно указывать через запятую или можно
передавать массив
Anime.alert(player: Player, title: String, description: String, seconds: Double) отправить сообщение с комментарием с
указанием времени
Anime.alert(player: Player, title: String, description: String) отправить сообщение с комментарием (на 7.3
секунды)
Anime.alert(player: Player, description: String, seconds: Double) отправить отписание с сообщением "Внимание" с
указанием вреени
Anime.alert(player: Player, description: String) комментарий с сообщением "Внимание" на 7.3 секунды
Anime.cursorMessage(player: Player, message: String) сообщения появляющиеся на курсоре и летящие вниз
Anime.itemTitle(player: Player, item: ItemStack, title: String?, subtitle: String?, duration: Double) отправить на
экран предмет с указанием продолжительности в секундах (можно указать сообщения сверху и снизу)
Anime.counting321(player: Player) начать на экране игрока отсчет 3 2 1 GO!
Anime.bigAlert(player: Player, message: String) отправляет текст на экран над хотбаром
Anime.killboardMessage(player: Player, text: String) отправить в правую верхнюю часть экрана сообщение
Anime.killboardMessage(player: Player, text: String, topMargin: Int) отправить в правую верхнюю часть экрана сообщение
с указанием отсупа сверху
Anime.timer(player: Player, text: String, duration: Int, red: Int, blue: Int, green: Int) начать игроку отчсет сверху
с сообщением, длительностью, цветом полоски
Anime.timer(player: Player, text: String, duration: Int) начать отсчет сверху с сообщением и продолжительностью
Anime.timer(player: Player, duration: Int) начать отсчет с сообщением по умолчанию
Есть 3 типа сообщений: FINE, WARN, ERROR. Каждое сообщение имеет свой цвет и символ в начале
Anime.systemMessage(player: Player, messageStatus: MessageStatus, text: String) отправить игроку системное сообщение
Anime.corpse(to: Player, name: String?, uuid: UUID, x: Double, y: Double, z: Double, secondsAlive: Int = 60) создать
труп по UUID трупа игрока с Cristalix и координат локации (имя трупу указывать необязательно) (secondsAlive - количество
секунд через которое исчезнет труп)
Anime.corpse(to: Player, name: String?, skinUrl: String, x: Double, y: Double, z: Double, secondsAlive: Int = 60) то
же самое, но можно ставить скин трупу по любой ссылке (имя трупу указывать необязательно)
Anime.clearAllCorpses(player: Player) очистить игроку все трупы
Закрыть игроку активное меню (кроме настроек и меню ачивок):
Anime.close(player: Player)
Отправить пустой Buffer игроку в канал:
Anime.sendEmptyBuffer(channel: String, player: Player)
Персонализация:
Anime.lockPersonalization(player: Player) заблокировать персонализацию игроку
Anime.unlockPersonalization(player: Player) разблокировать персонализацию игроку
Загрузка фотографий:
Anime.loadTexture(player: Player, url: String) загрузить игроку фотографию по ссылке в папку cache/animation/
Anime.loadTextures(player: Player, vararg url: String) загрузить игроку фотографии по ссылкам в
папку cache/animation/
Когда игрок скачает фотографию/и от отправит серверу пустой буффер в канал func:loaded
Пример получения фото на моде: ResourceLocation.of("cache/animation", "файл.png")
Регистрация канала для входящих сообщений:
createReader(channel: String, listener: PluginMessageListener)








































