Свой эффект зелья

Свой эффект зелья

Версия(и) Minecraft
1.7.10
Доброго времени суток! о/

В этом туториале я расскажу вам как создать собственный эффект, а также добавить предмет для использования этого зелья.

Расширение слотов:
Для начала, нам лучше бы расширить кол-во ванильных слотов под зелья, так как изначально их всего 32. Если вы уверены, что в сборке с вашим модом больше не будет зелий или наоборот, стоят такие моды как таумкрафт или ботания (вы делаете к ним аддон) - смело пропускайте этот шаг.
Java:
// Код взят у Vazkii и слегка переделан
static void extendPotionArray() {
    Potion[] potionTypes = null;
    for (Field f : Potion.class.getDeclaredFields()) { // получаем все поля из класса зелья
        f.setAccessible(true); // даём к ним доступ (локально убираем private)
        try {
            if (f.getName().equals("potionTypes") || f.getName().equals("field_76425_a")) { // проверка что это поле именно со списком зелий
                Field modfield = Field.class.getDeclaredField("modifiers");
                modfield.setAccessible(true);
                modfield.setInt(f, f.getModifiers() & ~Modifier.FINAL); // убираем final модфикатор
                potionTypes = (Potion[])f.get(null);
                final Potion[] newPotionTypes = new Potion[256]; // создаём новый массив зелий на 256 элементов
                System.arraycopy(potionTypes, 0, newPotionTypes, 0, potionTypes.length); // копируем старые зелья в новый массив
                f.set(null, newPotionTypes); // и заменяем
                return;
            }
        } catch (Exception e) {
            System.err.println(e); // на случай ошибки выводим её в лог
        }
    }
}
Добавьте вызов этого метода в ваш preInit чтобы убедиться, что слотов хватит:
if(Potion.potionTypes.length < 256) extendPotionArray();
Итак, когда мы убедились, что у нас точно не возникнет ArrayIndexOutOfBoundsException, можно приступать к созданию самого зелья.

Создание и регистрация эффекта:
Мы будем делать зелье полёта, позволяющее нам летать как в креативе.
В классе регистрации вашего мода объявите новую переменную:
public static Potion flight;
В preInit методе (после проверки, если она у вас есть) присвоим зелью экземпляр собственного класса зелья:
possession = new PotionFlight();
У вас высветится ошибка, но пока что игнорируйте её.
Давайте для начала создадим базовый класс для всех наших зелий:
Java:
public class PotionMod extends Potion {
    // это файл с текстурами наших иконок
    private static final ResourceLocation icons = new ResourceLocation(ModID, "path/to/your/icons.png");

    // конструктор зелья: ID в массиве зелий, его название, хороший эффект или плохой, цвет партиклов и индекс иконки
    public PotionMod(int id, String name, boolean badEffect, int color, int iconIndex) {
        super(id, badEffect, color);
        setPotionName("potion." + name);
        setIconIndex(iconIndex % 8, iconIndex / 8);
    }

    /* *
    * Функция получения иконки зелья для инвентаря. В ней мы привязываем нашу текстуру
    * Небольшой лайфхак, можно было бы использовать renderInventoryEffect, но по мне этот вариант проще
    */
    @Override
    @SideOnly(Side.CLIENT)
    public int getStatusIconIndex() {
        Minecraft.getMinecraft().renderEngine.bindTexture(resource);
        return super.getStatusIconIndex();
    }
}

Немного про иконки:
Мне никогда не нравилась эта система индексирования, но вкратце, у вас есть [неограниченное] поле размером 256x256, в котором вы можете размещать иконки зелий 18x18 в строгом порядке, после чего майнкрафт сам будет получать нужную область картинки. Верхняя левая картинка - 0, правее - 1, ещё правее - 2 и так далее. Ряд состоит из 8 картинок, после чего происходит смещение вниз. "Шаблон" такого поля можно найти в папке с текстурами по пути \assets\minecraft\textures\gui\container\inventory.png
Если для вас это слишком сложно, используйте метод renderInventoryEffect, предоставляемый форджем.

Продолжаем:
А теперь исправляем ошибку выше и создаём класс нашего зелья PotionFlight:
Java:
public class PotionFlight extends PotionMod {

    public PotionFlight() {
        // Совет: вынесите ID в конфиг, чтобы при случае его можно было поменять
        super(40, "flight", false, 0xFFFFEE, 2);
    }
}
Готово! Ваше зелье создано, зарегистрировано и может быть наложено. Но пока что оно ничего не делает. Время добавить функционал!

Добавление функционала:
Для этого изменим наш класс PotionFlight таким образом:
Java:
public class PotionFlight extends PotionMod {

    public PotionFlight() {
        super(40, "flight", false, 0xFFFFEE, 2);
        // Регистрируем этот класс обработчиком событий чтобы можно было управлять поведением зелья
        MinecraftForge.EVENT_BUS.register(this);
    }

    // Добавляем эвент обновления живого существва
    // Здесь мы проверяем, что обновился игрок и что у него есть наше зелье
    // В случае когда время ещё есть даём креативный полёт, иначе забираем и чистим эффект зелья
    @SubscribeEvent
    public void onEntityUpdate(LivingUpdateEvent e) {
        if (e.entityLiving instanceof EntityPlayer) {
            if (e.entityLiving.getActivePotionEffect(this) != null && e.entityLiving.getActivePotionEffect(this).getDuration() > 0) {
                ((EntityPlayer) e.entityLiving).capabilities.allowFlying =
                ((EntityPlayer) e.entityLiving).capabilities.isFlying = true;
            } else {
                ((EntityPlayer) e.entityLiving).capabilities.allowFlying =
                ((EntityPlayer) e.entityLiving).capabilities.isFlying = false;
            }
        }
    }
}

Есть второй способ добавления функционала на случай если вам не нужна особо гибкая обработка, как в этом примере.
  • isReady - метод, который вызывается каждый тик, служит для проверки можно ли применить эффект в данный момент (например, для применения раз в 10 тиков). Параметры - время действия и модификатор уровня.
  • performEffect - вызывается каждый тик, но только если isReady вернул до этого true. Служит для выполнения действий (например, для лечения). Параметры - сущность под эффектом и модификатор.
  • isInstant - мгновенный ли этот эффект (вроде моментального лечения).
  • affectEntity - обычно вызывается единожды для применения мгновенного эффекта. Параметры - тот кто применил зелье, тот на кого его применили, модификатор и коэффициент эффективности (зависит, например, насколько далеко от сущности упало взрывное зелье)
  • applyAttributesModifiersToEntity - вызывается единожды при наложении эффекта. Обычно служит для добавления модификаторов, вроде доп. сердец. Параметры - сущность под эффектом, список атрибутов (идентичен списку при вызове getAttributeMap у сущности), модификатор.
  • removeAttributesModifiersFromEntity - вызывается единожды при снятии эффекта. Служит для удаления модификаторов. Параметры те же.
Пример нашего зелья на функциях из самого Potion.class:
Java:
public class PotionFlight extends PotionMod {

    public PotionFlight() {
        super(40, "flight", false, 0xFFFFEE, 2);
    }
    
    @Override
    public boolean isReady(int time, int mod) {
        return true; // всегда готово
    }
    
    @Override
    public void performEffect(EntityLivingBase living, int mod) {
        // каждый тик заставляем лететь (немного читерно для данжа из таумкрафта, лучше так не делать)
        if (living instanceof EntityPlayer) ((EntityPlayer) living).capabilities.isFlying = true;
    }
    
    @Override
    public void applyAttributesModifiersToEntity(EntityLivingBase living, BaseAttributeMap map, int mod) {
        // даём полёт
        if (living instanceof EntityPlayer) {
            EntityPlayer player = ((EntityPlayer) living);
            player.capabilities.allowFlying = true;
            player.capabilities.isFlying = true;
            player.sendPlayerAbilities();
        }
    }
    
    @Override
    public void removeAttributesModifiersFromEntity(EntityLivingBase living, BaseAttributeMap map, int mod) {
        // забираем полёт
        if (living instanceof EntityPlayer) {
            EntityPlayer player = ((EntityPlayer) living);
            player.capabilities.allowFlying = false;
            player.capabilities.isFlying = false;
            player.sendPlayerAbilities(); // синхронизация атрибутов. Без неё полёт не снимается.
        }
    }
}
Кода тут побольше, зато нагрузка на сервер меньше, так что лучше использовать эти методы, а там как вам удобнее.

Отлично! Теперь, если игрок будет под эффектом нашего зелья он сможет летать.
Эффект готов, а это значит что пора обеспечить возможность его получение в самой игре!

Накладывание на игрока:
Думаю, как создавать и регистрировать предметы рассказывать смысла нет, поэтому вкратце расскажу как сделать предмет, дающий эффект зелья при выпивании:
Java:
public class ItemFlight extends ItemFood { // наследуем еду

    public ItemFlight() {
        // Оно не будет восстанавливать голод и сытость, и им нельзя кормить волка
        super(0, 0, false);
        // Добавление зелья к еде: ID, длительность (с), сила (-1 чтобы убрать частицы), шанс наложения (1.0 - 100%)
        setPotionEffect(40, 10, -1, 1.0F);
    }
   
    @Override
    public EnumAction getItemUseAction(ItemStack stack) {
        return EnumAction.drink; // анимация и звук питья
    }
}

Вот и всё! Осталось дропнуть этот предмет с какого-нибудь босса, и когда игрок его выпьет - получит креативный полёт на 10 секунд.

Всем спасибо за внимание!
  • Like
Реакции: Илья ~ YeanLondi
Автор
AlexSoсol
Просмотры
4,533
Первый выпуск
Обновление
Оценка
5.00 звёзд 3 оценок

Другие ресурсы пользователя AlexSoсol

Последние обновления

  1. Альтернативный вариант добавления функционала

    Добавил информацию про методы из Potion.class, что, по идее, снижает нагрузку на сервер. За инфу...

Последние рецензии

Очень полезная информация! )
Спасибо)
Думаю пригодится
Очень помогло, спасибо!
Сверху