Cats: The Butterfly’s Natural Enemy

I wanted to have some interaction between butterflies and vanilla entities to make them feel like part of the Minecraft world. An easy way to have an entity interact is to make butterflies the target of a predator. Our furry friends seemed like the best candidate for this.


I have an irrational fear of moths. Not butterflies, specifically just moths. It makes zero sense, but I guess that’s why they call it “irrational”. When a moth would invade my home, I would set the cat on it. The cat loved chasing it and, when finally caught, would eat the crunchy treat. So naturally, I figured since cats in the real world chase butterflies, they should do so in Minecraft as well.

In order to do this we need to change the cat’s behaviour. Mobs in minecraft use a simple AI model to control their actions. They have a series of prioritised goals that they will pursue if the conditions are met. For example, a cats goals include sleeping, attacking a target, or wandering around randomly.

The higher priority goals take precedence, and as long as the requirements are met, these goals will run. So a cat will attack a target if there is one available, otherwise it will focus on lower priority goals, such as following its owner, or wandering around.

Some goals (usually attack goals) will have targets. Each target will also have a priority and a custom goal to control how a target is selected. With multiple targets, an entity can be told to choose from a selection of other entities, such as the last attacker, the player, villagers, or rabbits.

It turns out that all we need to do here is to add a new target to the cat’s behaviour. We can do this in the onJoinWorld event on the Forge bus.

/**
 * Holds event listeners for entities.
 */
@Mod.EventBusSubscriber(modid = ButterfliesMod.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EntityEventListener {

    /**
     * On joining the world modify the cat's goals so it attacks butterflies.
     * @param event Information for the event.
     */
    @SubscribeEvent
    public static void onJoinWorld(EntityJoinLevelEvent event) {
        if (event.getEntity() instanceof Cat cat) {

            //  Add new butterfly target.
            cat.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(cat, Butterfly.class, false, null));
        }
    }
}

The NonTameRandomTargetGoal allows us to select an entity by class, and decide if we need to be able to see the target or not (the boolean parameter). The last parameter is a Predicate that allows us to give certain conditions for a target. For example, you can have cats attack turtles, but use the predicate to check if they are babies and if they are on land before they are chosen as a target.

And that’s it! A nice simple change this week. There are still a couple of small details I want to finish off, but we are getting closer to that first release.