如何为动物添加可以吸引它们的物品

Si_hen


版本信息
你使用的系统:Windows 10 1909 64位版
你是用的JDK: Java HotSpot™ 64-Bit (build 1.8.0_251-b08)
你使用的IDE:IntelliJ IDEA
你使用的IDE版本: 2020.2
Forge版本: 31.2.0
Minecraft版本: 1.15.2
Mapping 文件版本: snapshot-20200514-1.15.1

情况简述
我想要添加一个物品,手持时可以吸引所有继承了 AnimalEntity 的动物实体,但是发现所有可以吸引动物实体的物品都是直接写死在实体类中的。所以我想问 Forge 有没有提供什么方法可以为一个动物实体添加可吸引的物品,或者是我应该怎么做才能大致实现我想要的功能。

查到的资料
ChickenEntity.java

public class ChickenEntity extends AnimalEntity {
    private static final Ingredient TEMPTATION_ITEMS = Ingredient.fromItems(Items.WHEAT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.BEETROOT_SEEDS);
    ...
    protected void registerGoals() {
      ...
      this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, false, TEMPTATION_ITEMS));
      ...
   }

FledgeXu


几个思路。

  1. 反射获取TEMPTATION_ITEMS获取这个类,然后加物品
  2. AT 修改private改成public,加物品
  3. 用Coremod加方法添加物品。

推荐等级从高到低,最后一个方法如果不是迫不得已不要用。


Si_hen


可是在某些动物实体类里没有TEMPTATION_ITEMS字段。
CowEntity.java

public class CowEntity extends AnimalEntity {
    protected void registerGoals() {
      ...
      this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.fromItems(Items.WHEAT), false));
      ...
   }

FledgeXu


那就分别对待,继续看调用链,反正想法就是看这些东西存在哪里,然后直接改存的地方的值。


Si_hen


最终的解决方案是订阅 EntityJoinWorldEvent 事件,为所有的 AnimalEntity 添加了一个新的 TemptGoal,新的 TemptGoal 不会影响旧的 TemptGoal。但是因为 EntityJoinWorldEvent 事件在实体加载时也会触发,所以需要做一些判断。

大致的代码如下:

public void onAnimalAppear(EntityJoinWorldEvent event) {
    if (event.getEntity() instanceof AnimalEntity) {
        AnimalEntity animalEntity = (AnimalEntity) event.getEntity();
        boolean alreadySetUp = false;
        for (Goal goal : animalEntity.goalSelector.goals) {
            if (goal instanceof TemptGoal) {
                TemptGoal temptGoal = (TemptGoal) goal;
                if (temptGoal.isTempting(new ItemStack(ItemRegistry.item.get()))) {
                    alreadySetUp = true;
                    break;
                }
            }
        }
        if (!alreadySetUp) {
            animalEntity.goalSelector.addGoal(3, new TemptGoal(animalEntity, 0.8D, false, Ingredient.fromItems(ItemRegistry.item.get())));
        }
    }
}

其中,GoalSelector 中的 goals 字段是 private 的,TemptGoal 中的 isTempting 方法是 protected 的,因此需要使用 AT 进行修改。

这个思路来自于 Quark 的代码,并根据实际情况做了些调整。

但是我还是想问问有什么办法能够不用 AT,毕竟 AT 可能会触发兼容性问题。


FledgeXu


我觉得这里用AT没什么太大的问题,遇见问题再说吧。


system


该主题在最后一个回复创建后7天后自动关闭。不再允许新的回复。