Skip to content

Latest commit

 

History

History
1121 lines (894 loc) · 63.9 KB

README.md

File metadata and controls

1121 lines (894 loc) · 63.9 KB

Cristalix Visual-Driver DOCS (актуальная версия 3.2.9.RELEASE)

image


Зачем нужно использовать этот инструмент?

  1. Чтобы добавлять новые глобальные моды взаимодействующих с сервером (например новая персонализация - граффити)
  2. Для упращения работы с модами, вынесенные утилиты вам помогут не писать лишний код
  3. Автоматическое обновление базового пакета модов (стандартный пакет, квесты, лутбокс и прочее)
  4. Единый стиль и оформление режимов

Как подключить? Какие модули есть?

Get Started with `gradle`

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

Скачивание, загрузка и отправка ваших модов - утилита ModLoader

Быстрый старт без подробностей

image

  // Вариант 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() отправить все прогруженные моды всем игрокам

Отправка данных на моды - конструктор ModTransfer

image

// Пример 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) }  

Режим DEBUG (Для удобной разработки)

Простым языком: вы кидаете мод в папку, API это видит и перезагружает мод у игроков.

Данный режим позволяет мнгновенно обновлять мод на клиенте игрока, что очень удобно для быстрого тестировая. Обычные этапы разработки: написание кода -> компиляция -> заливка мода в папку сервера -> перезагрузка сервера -> вход на сервер -> загрузка мода, всего 6 этопов. С данным режимом: написание кода -> компиляция -> заливка мода в папку сервера -> смотрим, всего 4 этапа. Так же можно автоматически загруждать мод в папку прямо из среды разработки, но это уже задача вашей среды. Чтобы включить режим быстрого тестирования, допишите в Anime#include кит Kit.DEBUG. По умолчанию папка mods является хранилищем тестовых модов, чтобы сменить стандартную папку - измените переменную среды MOD_TEST_PATH.

TokenGroup EXPERIMENTAL

Новый скорборд для тайконов и симуляторов! ВАЖНО! Группу нужно создать только один раз, не нужно ее делать на каждого игрока

Пример создания на 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)   

ScoreBoard STANDARD

Сделали скорборд? Подписали на него игроков? - Ну все, теперь работает

Создание

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) // по схеме (переменная объявлена в примере выше)

Прогресс Bars EXPERIMENTAL

На данный момент реализован только реактивный прогрессбар (меняете поля на сервере - меняются на клиентах), работает на экране игрока и в мире (если указать 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)

Меню выбора / Меню выбора подрежима STANDARD

Selection

Choicer

  1. Чтобы загрузить свою текстуру, используйте Anime#loadTextures или Anime#loadTexture (возвращает texture).
  2. Если вы хотетите сменить валюту, то нужно изменить поле vault в Selection на символ валюты или путь к текстуре
  3. Если нужно закрыть меню, используйте 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);

Меню реконнекта STANDARD

Пример для языка kotlin

var menu = Reconnect { player -> player.sendMessage("Возвращаем вас в игру!") } // Первый вариант конструктора
menu = Reconnect(90) { player -> player.sendMessage("Возвращаем вас в игру!") } // Теперь указываем сколько секунд осталось
menu = Reconnect("Продать почку", 90, "Продать") { player -> player.sendMessage("Возвращаем вас в игру!") } // Теперь указываем все что только можно
   
menu.open(player) // Открываем меню игроку

Меню подтверждения STANDARD

Позволяет удостовериться, в корректности мотивов игрока. Поддерживает несколько строк.

Пример для языка 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); // Открываем меню игроку

Панели EXPERIMENTAL

Панели над хотбаром, их можно указывать сколько угодно, вся ширина делится в таком же отношении, в котором делится текст ко всему тексту. Можно реактивно менять текст/прогресс/цвет, стратегию реализуйте сами хук в сеттер/по таймингу.

val moneyPanel = ReactivePanel.builder()
        .color(GlowColor.GOLD)
        .progress(0.5)
        .text(Emoji.COIN + " " + stat?.money)
        .build()
        
moneyPanel.send(player)

Батлпасс BATTLEPASS

Что представляет из себя батлпасс?
Это приобретаемый набор заданий, за выполнение которых даются внутриигровые вещи.

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).
Можно указывать награду на каждый уровень, требуемый опыт.

Баннеры STANDARD

Что представляет из себя баннер?
Это голограмма, рисунок в мире, маркер и прочее что является прямоугольником в мире.

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

Индикаторы STANDARD

Список доступных индикаторов (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) показывает игроку указанные через запятую индикаторы

Сферы STANDARD

Сфера

Методы взаимодействия с клиентом:
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) плавно переместить сферу по указанным координатам за указанное количество секунд

Мультичат MULTICHAT

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) отправить всем игрокам на сервере сообщение в чат по ключу чата

Подсвечивание границ экрана, виньетка (модуль STANDARD)

Цвета:

  1. Цвета состоят из трез целых чисел и одного дробного, красный [0 .. 255], синий [0 .. 255], зеленый [0 .. 255], прозрачность [0 .. 1.0]
  2. Вы можете еспользовать 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

Подсвечивание границ места в мире EXPERIMENTAL

Методы добавления места в глобальный список мест (не показывает игрокам!):
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) очистить указанным игрокам места и удалить все места их глобального списка

Оверлей Бустеров (модуль EXPERIMENTAL)

Показывает текущее состояние бустера игрока.

Пример для языка Kotlin:

val boosterBar = boosterBar {
    segments = listOf()
    title = ""
    subtitle = ""
    isShowBackground = false
    progress = 1.0
}

boosterBar.open(player)

Индикатор бустеров

Показывает текущее состояние бустера игрока.

Метод:

Anime.startBoosters(player: Player, vararg boosters: Booster) показать индикатор

Перезарядка над ActionBar (модуль EXPERIMENTAL)

Методы:

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

Информация справа снизу (модуль STANDARD)

Методы:
Устаревший метод, который выводит сообщение только в правом нижнем углу: 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.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()

Диалоги DIALOGS

Методы взаимодействия с клинтом:
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)
        )
    )
))

Меню ежедневных наград STANDARD

Показ окна наград:

DailyRewardMenu.builder()
   .rewards(buttons...) // 7 кнопок с наградами
   .taken(false) // награда еще не взята
   .currentDay(1) // текучая награда - это первый день (начинается с 1)
   .build()
   .open(player); // открыть игроку

func:reward:click в этот канал прийдет callback, а именно - номер для (int), словить его можно с Anime#createReader, УЯЗВИМОСТЬ игрок может написать чит, и сам отправлять в данный канал инфу, проверяйте, может ли игрок получить награду!

Уведомления (зависит от предустановленного всеми cristalix-socials)

Уведомления возникают у игрока справа сверху, можно отправлять несколько, уведомление содержит 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)
)

Рассмотрим второй вариант:

  1. Создание шаблона уведомления
Alert.put(
    "hello", // ключ
    NotificationData( // содержимое
        null, 
        "notify",
        "Привет %nick%,\nкак дела?",
        toRGB(GlowColor.GREEN), 
        toRGB(GlowColor.GREEN), 
        3000, 
        listOf(
            button("Принять", "/ok", GlowColor.GOLD) // создание кнопки
        ), 
        "Вам уведомление!"
    )
)
  1. Получение шаблона, замена текста, отправка:
Alert.find("hello") // найти шаблон по ключу
    .replace("%nick", player.name) // заменить placeholder
    .replace("как дела?", "как спалось?")
    .send(player) // отправить игроку

Лутбокс LOOTBOX

Метод показа дропа с лутбокса:
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 полей приведёт к нечитабельности статистки!

Маркеры в мире STANDARD

!!! ВАЖНО !!!
Это частный случай 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 маркера

Всплывающие сообщения STANDARD

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) отправляет текст на экран над хотбаром

Системная информация STANDARD

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) отправить игроку системное сообщение

Прочее STANDARD

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)