The Inedible Crow Butterfly

I set out to have an easy week by adding a new butterfly. However, as ideas flowed in, this butterfly became a little more complex to implement. The result is a new butterfly with a unique ability that enables it to survive for longer.

In real life, the Mimic Butterfly implemented last week often imitates the Common Crow Butterfly. So I decided I would add this butterfly to the mod as well. After doing a bit of research on the butterfly I ended up adding a few features to support this new butterfly.

Poisonous


The Common Crow is the Asian version of the Monarch Butterfly. And like the Monarch, it can be poisonous if eaten. So, like the Monarch, its caterpillar can be used to brew poison potions.

The code originally implemented this recipe by getting the index of the Monarch using its ID. However, this means that we would have to manually add every ID we wanted to be poisonous in code manually. Rather than doing this, I implemented a new trait, POISONOUS, that could be used to create recipes for any butterflies or moths with the trait.

Since the recipes are loaded earlier than the Butterfly Data, I used code generation to create an array of traits, similar to how I did it for habitats last week. I also renamed the ButterflySpeciesList to ButterflyInfo, since the name didn’t make sense any more with all the extra information it held.

With that out of the way, I could just update the event handler for FMLCommonSetupEvent to check for this trait rather than hard coding the behaviour for specific butterflies:

        // Check for the poisonous trait
        for (int i = 0; i < ButterflyInfo.TRAITS.length; ++i) {
            if (Arrays.asList(ButterflyInfo.TRAITS[i]).contains(ButterflyData.Trait.POISONOUS)) {
                BrewingRecipeRegistry.addRecipe(
                        Ingredient.of(PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD)),
                        Ingredient.of(itemRegistry.getBottledButterflies().get(i).get()),
                        PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.POISON));
            }
        }

It’s only been one week and already the trait system is paying off.

Inedible


One of the reasons the Mimic Butterfly likes to imitate the Crow Butterfly is because the Crow is inedible. Since the Crow is inedible, it won’t be attacked by other creatures. I added a new trait, INEDIBLE to support this feature.

With this trait, I created a predicate that could be used to tell creatures to ignore butterflies with this trait.

    /**
     * Used to stop entities from attacking inedible butterflies.
     * @param entity The entity the cat wants to target.
     * @return TRUE if the entity is any butterfly except for a Forester.
     */
    private static boolean isButterflyEdible(LivingEntity entity) {
        if (entity instanceof Butterfly butterfly) {
            return !butterfly.getData().hasTrait(ButterflyData.Trait.INEDIBLE);
        }

        return false;
    }

The Cat’s predicate was also updated in order to take into account the INEDIBLE trait:

    /**
     * Used to stop cats from attacking forester butterflies.
     * @param entity The entity the cat wants to target.
     * @return TRUE if the entity is any butterfly except for a Forester.
     */
    private static boolean isButterflyAttackableByCat(LivingEntity entity) {
        if (entity instanceof Butterfly butterfly) {
            return !butterfly.getData().hasTrait(ButterflyData.Trait.CATFRIEND) &&
                    isButterflyEdible(entity);
        }

        return false;
    }

Now I can pass in this new predicate to any creatures that would normally attack butterflies, and they will ignore any that have this trait. For example, the Fox’s attack behaviour now uses this predicate:

        //  Foxes
        if (event.getEntity() instanceof Fox fox) {
            fox.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(
                    fox, Butterfly.class, false, EntityEventListener::isButterflyEdible));
        }

There is still one other creature, however. Frogs use a tag to determine what animals it can attack. At the moment, our Python script adds all butterflies to this tag. This needed to be modified to take into account the new trait so that Frogs would also ignore them. To do this, I now iterate over every butterfly and check their traits before adding them to the list:

    # Iterate over so we can exclude inedible butterflies.
    for butterfly in species:
        for i in range(len(folders)):
            folder = folders[i]
            try:
                with open(BUTTERFLY_DATA + folder + butterfly + ".json", 'r', encoding="utf8") as input_file:
                    json_data = json.load(input_file)

                # Check for the inedible trait before we add it.
                traits = json_data["traits"]
                if "inedible" not in traits:
                    values += ["butterflies:" + butterfly]

            except FileNotFoundError:
                # doesn't exist
                pass
            else:
                # exists
                pass

So we now have a poisonous, inedible butterfly. But these traits add yet another feature to this new butterfly.

Slow


Since nothing really attacks them, Common Crow butterflies take their time when flying around. Up until now, butterflies have only had MODERATE or FAST available as possible speeds. For the Common Crow I added SLOW speed, which means exactly what you think it means.

        switch (getData().speed()) {
            case SLOW -> navigation.setSpeedModifier(0.8);
            case MODERATE -> navigation.setSpeedModifier(1.0);
            case FAST -> navigation.setSpeedModifier(1.2);
        }

Design


For the Common Crow, I wanted it to look a bit like the Common Mime, but different enough that players could tell them apart. I noticed from pictures that the Common Crow seemed lighter, and had more brown on its body, so I used that as inspiration to modify the design.

The caterpillar was fun to work on. I love how its a vibrant red with a dark black head and I tried to reflect that in its design.

After doing all this work it was great to finally see them in game, and now you can too in the latest version of Bok’s Banging Butterflies!