Версия(и) Minecraft
1.7.10, 1.12.2
Доброго времени суток. На этот раз я буду описывать процесс создания собственных чар. Разобранный пример и другие, более сложные, вы сможете найти на GitHub: 1.7.10 и 1.12.2.

Чары
Зачаровывание предметов является неотъемлемой частью игры. Есть важный момент - чары привязаны только к предмету (NBT ItemStack'а). Хоть это и вполне логично, это серьёзно ограничивает гибкость их применения так как для этого обязательно придётся использовать эвенты (за исключением чар повышения и снижения урона, предлагающих встроенные методы), а каких либо встроенных средств для отслеживания активных чар и их сохранения (относительно сущностей) в игре нет.

Создание чар

Все чары наследуются от класса Enchantment, который определяет такие основные свойства как редкость чар, допустимые предметы для применения, уровни чар и требуемый опыт. Применение чар к определённым предметам довольно просто настраивать. Соответствующие зачарованные книги создаются автоматически при регистрации чар.

В рамках туториала создадим чары "Безопасное Падение", которые будут накладываться на любые ботинки и убирать урон при падении, но добавим изюминку - ботинки будут терять прочность в зависимости от высоты.

1.7.10
Начинается всё с класса, представляющего чары. Класс чар EnchantmentSafeFall:
Java:
public class EnchantmentSafeFall extends Enchantment {

    public EnchantmentSafeFall(String enchantmentName, int enchantmentId, int weight, EnumEnchantmentType enchantmentType) {
      
        super(enchantmentId, weight, enchantmentType);
      
        this.setName(enchantmentName);
    }
}


Суперконструктор требует указания редкости идентификатора чар, "веса" чар и типа целевых предметов, на которые эти чары можно накладывать.

Первым идёт идентификатор (id) чар. В суперклассе содержится массив для чар размером в 256 ячеек, но занято не так много. Изучите его, последние ванильные чары имеют id равный 62, хотя разбросаны они беспорядочно.

Рассмотрим второй параметр - вес, влияющий на редкость чар. Чем ниже это значение - тем реже встречаются чары (в столе зачаровывания). Ванильные чары имеют разброс от 1 (Шёлковое Касание) до 10 (Острота, Защита и т.п.).

Класс предметов экипировки, для которых подходят чары, идёт третьим параметром EnumEnchantmentType. Там много доступных вариантов, например all - чары могут быть наложены на всё, digger - только на инструменты, armor - на броню и т.д.

Все эти параметры будут определены в конструкторе при инициализации. Я добавил строковый параметр для имени чар и метод для его указания.

Далее нужно определиться с какими чарами эти чары могут конфликтовать. По умолчанию все чары конфликтуют сами с собой. Что бы ограничить применение других чар на предмет при наличии конфликтных или наоборот используется метод Enchantment#canApplyTogether():
Java:
    @Override
    public boolean canApplyTogether(Enchantment enchantment) {
      
        return super.canApplyTogether(enchantment) && enchantment != Enchantment.featherFalling;
    }


В этом примере я исключил ванильные чары, влияющие на снижение урона от падения.

Если предполагается что чары имеют несколько уровней, то необходимо переопределить Enchantment#getMinLevel() и Enchantment#getMaxLevel(). По умолчанию они возвращают значение, равное единице. Наши чары не будут иметь уровней (будет один по умолчанию) и переопределять эти методы нет необходимости.

Что бы задать границы уровней, в которых при зачаровывании можно получить чары нужно переопределить Enchantment#getMinEnchantability() и Enchantment#getMaxEnchantability():
Java:
    @Override
    public int getMinEnchantability(int enchantmentLevel) {
      
        return 10;
    }

    @Override
    public int getMaxEnchantability(int enchantmentLevel) {
      
        return 20;
    }


Так как уровень у чар только один, можно не заморачиваться хитрыми расчётами.

В итоге класс целиком:
Java:
public class EnchantmentSafeFall extends Enchantment {
  
    /*
     * Падение не наносит урона. Эффект применяется с помощью эвента в EnchantmentsEvents.
     *
     *
     * Кол-во уровней чар: 1
     *
     * Уровни опыта для зачаровывания: 10 - 20
     *
     * Несовместимости: Невесомость.
     *
     * Целевые предметы: Любые ботинки.
     *
     * Эффект: Полное исключение урона при падении, но ботинки получают урон равный высоте, с которой упал игрок.
     */

    public EnchantmentSafeFall(String enchantmentName, int enchantmentId, int weight, EnumEnchantmentType enchantmentType) {
      
        super(enchantmentId, weight, enchantmentType);
      
        this.setName(enchantmentName);
    }
  
    @Override
    public boolean canApplyTogether(Enchantment enchantment) {
      
        return super.canApplyTogether(enchantment) && enchantment != Enchantment.featherFalling;
    }
  
    @Override
    public int getMinEnchantability(int enchantmentLevel) {
      
        return 10;
    }

    @Override
    public int getMaxEnchantability(int enchantmentLevel) {
      
        return 20;
    }
}

Регистрацию чар будем проводить в отдельном классе EnchantmentsRegistry:
Java:
public class EnchanmtmentsRegistry {

    public static final Enchantment
    SAFE_FALL = new EnchantmentSafeFall("safe_fall", 100, 5, EnumEnchantmentType.armor_feet);//Безопасное Падение
  
    public static void register() {}
}


Задаём имя, id (я начал со 100), вес и допустимую экипировку. При загрузке класса наши чары инициализируются и суперконструктор Enchantment добавит наши чары в общий массив и создаст соответствующую зачарованную книгу. Для зарузки класса я создал пустой метод register(), который следует вызвать в процессе преинициализации мода:
Java:
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
      
        EnchanmtmentsRegistry.register();
    }


Локализация названия чар:
enchantment.safe_fall=Безопасное Падение

А использования в команде /enchant для зачаровывания предмета у себя в руке:
/enchant (ваш ник) 100(id чар)

Применение эффекта

Для удобства я создал отдельный класс, в котором размещаю удобные статические методы для проверки наличия чар EnchantmentsHelper. Выглядит это так:
Java:
public class EnchantmentsHelper {
  
    /*
     * Вспомогательный класс для быстрой проверки наличия определённых чар на экипировке игрока.
     */

    public static boolean hasSafeFallEnchantment(EntityPlayer player) {
      
        return EnchantmentHelper.getEnchantmentLevel(EnchanmtmentsRegistry.SAFE_FALL.effectId, player.inventory.armorItemInSlot(0)) > 0;
    }
}


По сути происходит запрос на уровень чар с искомым идентификатором на определённом элементе. Если искомых чар нет, то вернётся уровень, равный нулю.

Теперь к эффекту. В данном случае надо отменить урон от падения - для этого есть LivingFallEvent. Поместил я его в отдельный класс (можно и в класс чар) EnchantmentsEvents:
Java:
public class EnchantmentsEvents {
  
    /*
     * Эвенты для применения эффектов чар.
     */

    //Отмена урона при падении и повреждение ботинок для чар Безопасное Падение (EnchantmentSafeFall).
    @SubscribeEvent
    public void onPlayerFall(LivingFallEvent event) {
      
        if (event.entityLiving instanceof EntityPlayer) {
          
            EntityPlayer player = (EntityPlayer) event.entityLiving;
                      
            if (EnchantmentsHelper.hasSafeFallEnchantment(player)) {//Проверка на наличие чар "Безопасного Падения".
              
                player.inventory.armorItemInSlot(0).damageItem((int) event.distance, player);//Дамаг по ботинкам в кол-ве равном расстоянию падения.
              
                event.setCanceled(true);//Отмена эвента (урона).
            }
        }
    }
}


Не забудьте зарегистрировать класс с эвентом:
Java:
    @EventHandler
    public void init(FMLInitializationEvent event) {
      
        MinecraftForge.EVENT_BUS.register(new EnchantmentsEvents());
    }


Вот и всё. Теперь можно и тестировать. В игре возьмите любые ботинки и зачаруйте командой. Если всё получилось - вы получите ботинки с созданными чарами. При падении с надетыми ботинками с этими чарами урон наносится не должен, но прочность ботинок будет снижаться в зависимости от высоты падения.

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

1.12.2
Начинается всё с класса, представляющего чары. Класс чар EnchantmentSafeFall:
Java:
public class EnchantmentSafeFall extends Enchantment {

    public EnchantmentSafeFall(String enchantmentName, Rarity rarity, EnumEnchantmentType type, EntityEquipmentSlot... slots) {
    
        super(rarity, type, slots);
    
        this.setEnchantmentName(enchantmentName);
    }

    private void setEnchantmentName(String enchantmentName) {
    
        this.setRegistryName(EnchantmentsMain.MODID, enchantmentName);
        this.setName(enchantmentName);
    }
}


Суперконструктор требует указания редкости чар, класса целевых предметов и элементы экипировки, на которые эти чары можно накладывать.

Рассмотрим первый параметр - редкость, представленная перечислением Rarity. Она бывает четырх разновидностей: COMMON, UNCOMMON, RARE и VERY_RARE. От редкости зависит вероятность получить эти чары во время зачаровывания.

Второй - класс предметов экипировки, для которых подходят чары, перечисление EnumEnchantmentType. Там много доступных вариантов, например ALL - чары могут быть наложены на всё, DIGGER - только на инструменты, ARMOR - на броню, WEARABLE - на всё, что можно надеть на голову и т.д.

Массив допустимых для чар предметов экипировки идёт третьим параметром, ознакомьтесь с содержимым перечисления EntityEquipmentSlot.

Все эти параметры будут определены в конструкторе при инициализации. Я добавил строковый параметр для имени чар и метод для его указания.

Далее нужно определиться с какими чарами эти чары могут конфликтовать. По умолчанию все чары конфликтуют сами с собой. Что бы ограничить применение других чар на предмет при наличии конфликтных или наоборот используется метод Enchantment#canApplyTogether():
Java:
    @Override
    public boolean canApplyTogether(Enchantment enchantment) {
    
        return super.canApplyTogether(enchantment) && enchantment != Enchantments.FEATHER_FALLING;
    }


В этом примере я исключил ванильные чары, влияющие на снижение урона от падения.

Если предполагается что чары имеют несколько уровней, то необходимо переопределить Enchantment#getMinLevel() и Enchantment#getMaxLevel(). По умолчанию они возвращают значение, равное единице. Наши чары не будут иметь уровней (будет один по умолчанию) и переопределять эти методы нет необходимости.

Что бы задать границы уровней, в которых при зачаровывании можно получить чары нужно переопределить Enchantment#getMinEnchantability() и Enchantment#getMaxEnchantability():
Java:
    @Override
    public int getMinEnchantability(int enchantmentLevel) {
    
        return 10;
    }

    @Override
    public int getMaxEnchantability(int enchantmentLevel) {
    
        return 20;
    }


Так как уровень у чар только один, можно не заморачиваться хитрыми расчётами.

В итоге класс целиком:
Java:
public class EnchantmentSafeFall extends Enchantment {

    /*
     * Падение не наносит урона. Эффект применяется с помощью эвента в EnchantmentsEvents.
     *
     *
     * Кол-во уровней чар: 1
     *
     * Уровни опыта для зачаровывания: 10 - 20
     *
     * Несовместимости: Невесомость, Покоритель глубин и Ледяная Поступь.
     *
     * Целевые предметы: Любые ботинки.
     *
     * Эффект: Полное исключение урона при падении, но ботинки получают урон равный высоте, с которой упал игрок.
     */

    public EnchantmentSafeFall(String enchantmentName, Rarity rarity, EnumEnchantmentType type, EntityEquipmentSlot... slots) {
    
        super(rarity, type, slots);
    
        this.setEnchantmentName(enchantmentName);
    }

    private void setEnchantmentName(String enchantmentName) {
    
        this.setRegistryName(EnchantmentsMain.MODID, enchantmentName);
        this.setName(enchantmentName);
    }

    @Override
    public boolean canApplyTogether(Enchantment enchantment) {
    
        return super.canApplyTogether(enchantment) && enchantment != Enchantments.FEATHER_FALLING;
    }

    @Override
    public int getMinEnchantability(int enchantmentLevel) {
    
        return 10;
    }

    @Override
    public int getMaxEnchantability(int enchantmentLevel) {
    
        return 20;
    }
}

Регистрацию чар будем проводить в отдельном классе EnchantmentsRegistry:
Java:
@Mod.EventBusSubscriber(modid = EnchantmentsMain.MODID)
public class EnchantmentsRegistry {

    public static final Enchantment
    SAFE_FALL = new EnchantmentSafeFall("safe_fall", Rarity.RARE, EnumEnchantmentType.ARMOR_FEET, new EntityEquipmentSlot[] {EntityEquipmentSlot.FEET});

    @SubscribeEvent
    public static void registerEnchantments(RegistryEvent.Register<Enchantment> event) {
    
        event.getRegistry().registerAll(
            
                SAFE_FALL
        );
    }
}


Задаём имя, редкость, допустимые элементы экипировки и регистрируем через эвент RegistryEvent.Register. Для автоматической подгрузки эвентов ставим аннотацию @Mod.EventBusSubscriber(modid = Main.MODID), а метод определяем статическим.

Локализация названия чар:
enchantment.safe_fall=Безопасное Падение

А использования в команде /enchant для зачаровывания предмета у себя в руке:
/enchant @s (ваш modid):safe_fall

Применение эффекта

Для удобства я создал отдельный класс, в котором размещаю удобные статические методы для проверки наличия чар EnchantmentsHelper. Выглядит это так:
Java:
public class EnchantmentsHelper {

    /*
     * Вспомогательный класс для быстрой проверки наличия определённых чар на экипировке игрока.
     */

    public static boolean hasSafeFallEnchantment(EntityPlayer player) {
    
        return EnchantmentHelper.getEnchantmentLevel(EnchantmentsRegistry.SAFE_FALL, player.inventory.armorItemInSlot(0)) > 0;
    }
}


По сути происходит запрос на уровень искомых чар на определённом элементе. Если искомых чар нет, то вернётся уровень, равный нулю.

Теперь к эффекту. В данном случае надо отменить урон от падения - для этого есть LivingFallEvent. Поместил я его в отдельный класс (можно и в класс чар) EnchantmentsEvents:
Java:
@Mod.EventBusSubscriber(modid = EnchantmentsMain.MODID)
public class EnchantmentsEvents {

    /*
     * Эвенты для применения эффектов чар.
     */

    //Отмена урона при падении и повреждение ботинок для чар Безопасное Падение (EnchantmentSafeFall).
    @SubscribeEvent
    public static void onPlayerFall(LivingFallEvent event) {
    
        if (event.getEntityLiving() instanceof EntityPlayer) {
        
            EntityPlayer player = (EntityPlayer) event.getEntityLiving();
                    
            if (EnchantmentsHelper.hasSafeFallEnchantment(player)) {//Проверка на наличие чар "Безопасного Падения".
            
                player.inventory.armorItemInSlot(0).damageItem((int) event.getDistance(), player);//Дамаг по ботинкам в кол-ве равном расстоянию падения.
            
                event.setCanceled(true);//Отмена эвента (и урона).
            }
        }
    }
}


Вот и всё. Теперь можно и тестировать. В игре возьмите любые ботинки и зачаруйте командой. Если всё получилось - вы получите ботинки с созданными чарами. При падении с надетыми ботинками с этими чарами урон наносится не должен, но прочность ботинок будет снижаться в зависимости от высоты падения.

Книга с чарами будет располагаться во вкладке с бронёй среди остальных. Вы можете наложить эти чары на наковальне. Ну или попытаться получить их на столе зачаровывания.

Послесловие

Вот как то так это и происходит. Если вам нужно больше информации, то ознакомьтесь с другими примерами в моём репозитории: 1.7.10, 1.12.2. Кроме этих там есть чары переплавки, которые накладываются только на кастомные инструменты и делают интересные вещи, а также чары для кастомного меча, аналогичные "Остроте" с демонстрацией ванильной системы применения чар без эвентов.

Всем спасибо за внимание. Если вам есть что добавить или какие либо вопросы - пишите в обсуждении.
Автор
AustereTony
Просмотры
1,987
Первый выпуск
Обновление
Оценка
5.00 звёзд 3 оценок

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

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

  1. Обновление #1

    Туториал дописан для версии игры 1.7.10.

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

Как всегда отлично
Пишешь очень полезные гайды. Хотелось бы видеть и на 1.7.10 твои туториалы.
AustereTony
AustereTony
Спасибо. Постепенно допишу и для 1.7.10, пока только команды успел.
Сверху