Minecraft Forge 1.7.2/1.7.10 Changing Drops of Vanilla Entities

Introduction

It seems to be a common interest to change the drops from vanilla entities.  This tutorial explains how to do this.

How Drops Are Controlled In the Code

The good news is that the drops list is a public ArrayList (of items) field in the Entity class and so it can be directly access and modified like any ArrayList.  The field is called capturedDrops.

However, it is not a static field or in a registry and so every instance of an Entity has its own capturedDrops list. So to modify all of an certain entity type, you'll have to ensure that you modify the capturedDrops for each entity instance.

At What Point Should You Change The Drops

If we want to change the drops list we need to find the appropriate event -- something that occurs after the entity has its capturedDrops list populated:
  • The EntityConstructing event isn't a good place for changing the drops because the drops assignment seems to happen after the constructor.  
  • The LivingSpawnEvent is also not a good choice because that can fire before the spawn even happens whether an entity instance is constructed or not.
In fact, the way the the capturedDrops list gets populated is a little bit complex -- because some entities may have a dynamic inventory, the drops list is calculated at the time that the drop occurs (usually entity death/break).  So the only place you should really modify the drops is in the LivingDropsEvent. I know that seems sort of obvious, but I think it is worthwhile to also understand what doesn't work and why.

Note: If you want to inspect the code where the vanilla drops list is populated, it is in the entityDropItem() method in the Entity class (and therefore inherited by all entities in the game), or in an overridden subclass.

Key point: The captureDrops list is populated at the time a drop happens (just before the LivingDropsEvent), so shouldn't be modified earlier.

Handle LivingDropsEvent To Change The Drops

The capturedDrops list is passed as the event.drops field in the LivingDropsEvent.

The drops field is an ArrayList of Item class elements so (like any ArrayList) you can easily clear() it, remove() items, and add() items or use any other regular methods for modifying an ArrayList.

Example 1: Clear All Drops For EntitySheep

Create an event handler for the LivingDropsEvent.  Basically you need a class containing the method example below that is registered to the EVENT_BUS.  To set that up see my tutorial on events: Jabelar's Event Handling Tutorial.  

The event handling method should contain something like this:

@SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true)
public void onEvent(LivingDropsEvent event)
{
    if (event.entity instanceof EntitySheep)
    {
        // DEBUG
        System.out.println("EntitySheep drops event");
        event.drops.clear();
    }
} 

It is that simple.  Just clear the list and that will remove the drops.

Example 2: Change Drop For EntitySheep To Be 5 Apples

In this case, the event handling method should just add items after clearing it.

@SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true)
public void onEvent(LivingDropsEvent event)
{
    if (event.entity instanceof EntitySheep)
    {
        // DEBUG
        System.out.println("EntitySheep drops event");
        event.drops.clear();
        ItemStack itemStackToDrop = new ItemStack(Items.apple, 5);
        event.drops.add(new EntityItem(event.entity.worldObj, event.entity.posX, 
              event.entity.posY, event.entity.posZ, itemStackToDrop));
    }
} 

Conclusion

Events make it easy to intercept vanilla actions and modify them.  To modify drops for vanilla entities, the LivingDropsEvent provides the right hook.  The drops field in that event is simply an ArrayList that can be modified like any other.  Have fun!

As always, if you see have any suggestions for corrections or clarifications to this tutorial let me know!

2 comments:

  1. Hi, I appreciate your tutorials, they've been very helpful in my attempts at learning forge mod development. I'm working on adding a custom item to a vanilla block. specifically to Tall Grass. I've set up a class for the HarvestDropsEvent and managed to add my item to the drops ArrayList, but the drop chance is always disproportionate between my item and the seeds that normally drop. For instance, if I don't change event.dropChance, seeds drop about 1/10 times and my item drops 10/10. If I Do change event.dropChance to 0.1F seeds drop very rarely, and my item drops about 1/10 times. would you mind explaining how to change that if there's a way to do it without clearing the existing items and adding them back? I'm trying to avoid changing things that might affect other mods in case any other mods have added a drop to TallGrass. Thanks again =)

    ReplyDelete
    Replies
    1. Ahh, nevermind, I just found the function MinecraftForge.addGrassSeed() to add to my base class, and that's more or less handling what I need it to do. Thanks anyway

      Delete