Minecraft Forge 1.7.2/1.7.10 Custom *Throwable* Spawn Egg

This is Draft / Incomplete


Introduction

In recent 1.7.2 Forge, it is considered better practice to register your entities using the registerModEntity() method instead of the older registerGlobalID() method.  The latter is limited to 255 IDs and therefore can cause trouble for those with multiple mods that introduce a lot of entities.

However, if you use the registerModEntity() method of registration there is not a built-in method for adding a spawn egg, so you need to make your own spawn egg item system.  It isn't really that hard, but there are a few steps involved.  This tutorial will explain them.

As a twist, in this example I have made the custom spawn eggs throwable similar to an egg item. I also always make the spawned entity a small child. Some people like this, some people don't but in any case with this tutorial you should be able to get the idea of how to make you own item for spawning your custom entities.

Tip: If you want to make a traditional spawn egg (not throwable) you should know that in the Minecraft code the spawn egg is called an ItemMonsterPlacer -- look at the code in that class for inspiration.

General Concept: The Item Generates A Throwable Entity Which Generates A Spawned Entity

To make a spawn item (egg or otherwise) you need to start with an Item so that it can be shown in a CreativeTab, can be held in the player inventory, be wielded by the player, and so forth. But when we use the item, we want it (in my case) to create an EntityThrowable -- i.e. something that will travel and act independently from the player or the item that is held.

This EntityThrowable will actually be invisible, although it would be possible to give it a model and texture if you so choose.  The invisible, EntityThrowable will then travel until it hits something in which case it will spawn the intended Entity you want to spawn and the EntityThrowable will "kill" itself.  Got that?

Create Spawn Egg Item

Create a custom class that extends Item, for example:

public class ItemWildAnimalSpawnEggThrowable extends Item
{
    public int colorBase;
    public int colorSpots;
    public String entityToSpawnName = "";
    protected String entityToSpawnNameFull = "";
    protected EntityWildAnimalsEgg entityEgg;
    private IIcon theIcon;
    
    public ItemWildAnimalSpawnEggThrowable(String parEntityToSpawnName, int parPrimaryColor, 
          int parSecondaryColor)
    {
        this.maxStackSize = 64;
        this.setCreativeTab(CreativeTabs.tabMisc);
        entityToSpawnName = parEntityToSpawnName;
        entityToSpawnNameFull = WildAnimals.MODID+"."+entityToSpawnName; 
        colorBase = parPrimaryColor;
        colorSpots = parSecondaryColor;
    }

    /**
    * Called whenever this item is equipped and the right mouse button is pressed. 
    * Args: itemStack, world, entityPlayer
    */
    @Override
    public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, 
          EntityPlayer par3EntityPlayer)
    {
        if (!par3EntityPlayer.capabilities.isCreativeMode)
        {
            --par1ItemStack.stackSize;
        }

        par2World.playSoundAtEntity(par3EntityPlayer, "random.bow", 0.5F, 0.4F / 
              (itemRand.nextFloat() * 0.4F + 0.8F));

        if (!par2World.isRemote)
        {
            entityEgg = new EntityWildAnimalsEgg(par2World, par3EntityPlayer);
            entityEgg.setEntityToSpawn(entityToSpawnNameFull);
            par2World.spawnEntityInWorld(entityEgg);
        }

        return par1ItemStack;
    }
    
    @Override
    @SideOnly(Side.CLIENT)
    public int getColorFromItemStack(ItemStack par1ItemStack, int parColorType)
    {
        return (parColorType == 0) ? colorBase : colorSpots;
    }

    @Override
    @SideOnly(Side.CLIENT)
    public boolean requiresMultipleRenderPasses()
    {
        return true;
    }
    
    @Override
    // Doing this override means that there is no localization for language
    // unless you specifically check for localization here and convert
       public String getItemStackDisplayName(ItemStack par1ItemStack)
       {
           return "Spawn "+entityToSpawnName;
       }  


    @Override
    @SideOnly(Side.CLIENT)
    public void registerIcons(IIconRegister par1IconRegister)
    {
        super.registerIcons(par1IconRegister);
        this.theIcon = par1IconRegister.registerIcon(this.getIconString() + "_overlay");
    }
    
    /**
     * Gets an icon index based on an item's damage value and the given render pass
     */
    @Override
    @SideOnly(Side.CLIENT)
    public IIcon getIconFromDamageForRenderPass(int par1, int par2)
    {
        return par2 > 0 ? this.theIcon : super.getIconFromDamageForRenderPass(par1, par2);
    }

}

Here are some explanations of some bits of the code above.

The Colored, Spotted Egg Effect

Since I wanted to use this spawn egg for multiple custom entities, I wanted to continue the Minecraft theme of using spotted eggs with different colors as the icon texture.  I could have simply created all the spotted egg images separately, but instead I chose to use the Minecraft method of combining the colorized egg shape with colorized spots overlay. To do this I:
  • Return true for the requiresMultipleRenderPasses() method,
  • Return either the colorBase or colorSpots depending on the render pass.  The getColorFromItemStack() method is called automatically by various rendering methods that render how the item appears when held, in inventory, etc.  The color is blended automatically.  Note that to colorize properly, that the actual texture assets should be in grey-scale.
  • Register the icon for the overlay (spots) texture.
  • Return either the base or overlay texture from getIconFromDamageForRenderPass() based on the render pass.  Note that "damage" in the Minecraft methods is also used for other purposes, such as in this case where it represents the texture selection.
  • Put the grey-scale textures PNG files into the proper asset packages (see details below).

The Color Fields

Tip: Colors in computers are often represented as RGB (red, green, blue) in a hexadecimal format.  To pick colors in this format, there are a number of color pickers available, for example I use: ColorPicker.  Just use such a tool to pick the color and then copy the hexadecimal value.

Tip: To represent a hexadecimal (integer) value in many programming languages, including Java, you precede the value by "0x".  For example, a nice color red is represented by 0xF50C27.

Tip: If there are certain colors you use a lot in your code, don't be afraid to make a constant to represent the value.

Create Spawn Egg Entity

Create a custom class that extends EntityThrowable, for example:

public class EntityWildAnimalsEgg extends EntityThrowable
{
    protected String entityToSpawnName = "";
    protected EntityAnimal entityToSpawn;

    public EntityWildAnimalsEgg(World par1World)
    {
        super(par1World);
    }

    public EntityWildAnimalsEgg(World par1World, EntityLivingBase par2EntityLivingBase)
    {
        super(par1World, par2EntityLivingBase);
    }
    
    public void setEntityToSpawn(String parEntityToSpawnName)
    {
        entityToSpawnName = parEntityToSpawnName;
    }
    
    public String getEntityToSpawn()
    {
        return entityToSpawnName;
    }

    /**
     * Called when this EntityThrowable hits a block or entity.
     */
    @Override
    protected void onImpact(MovingObjectPosition par1MovingObjectPosition)
    {
        if (par1MovingObjectPosition.entityHit != null)
        {
            par1MovingObjectPosition.entityHit.attackEntityFrom(
                  DamageSource.causeThrownDamage(this, getThrower()), 0.0F);
        }

        if (!worldObj.isRemote) // never spawn entity on client side
        {
            entityToSpawn = (EntityAnimal) EntityList.createEntityByName(entityToSpawnName, 
            worldObj); 
            entityToSpawn.setGrowingAge(-24000);
            entityToSpawn.setLocationAndAngles(posX, posY, posZ, rotationYaw, 0.0F);
            worldObj.spawnEntityInWorld(entityToSpawn);
        }

        for (int j = 0; j < 8; ++j)
        {
            worldObj.spawnParticle("snowballpoof", this.posX, this.posY, this.posZ, 
                  0.0D, 0.0D, 0.0D);
        }

        if (!worldObj.isRemote)
        {
            setDead();
        }
    }
} 
The code should be fairly self explanatory.  But here are a few sections of clarification.

The Constructors

The constructors are inherited from the superclass and simply call super constructors.  You can follow the declarations in Eclipse to understand the different effects of each.

The constructor of interest to us is the one that passes the EntityLivingBase parameter, because that is meant to be the entity that is doing the throwing and so the direction and position from which the throw launches is set accordingly.  This constructor is called by our Item onRightClick() method as already explained above.

The onImpact() Event Method

Tip: Methods that are named starting with "on" are usually associated with an event and are automatically called in response to that event.  In this case, since the method onImpact() is already provided you don't have to worry about detecting the impact, or handling an event, or calling the method -- all you have to do is put your code into the method and it will automatically be called when needed.

In my example, the onImpact()method contains code that:
  • Counts the impact as an attack (with no damage) in case that might be important.
  • Checks to ensure the code is running on server side before spawning, and the spawning consists of:
    1. Create an instance the spawned entity class
    2. Optionally (if your class is a subclass of EntityAgeable)setting its age. In my example I make it always a young child.  Feel free to change this to set a different age, randomize it, etc.
    3. Setting the position of the spawned entity to match the position of the impact.
    4. Importantly, actually call the spawnEntityInWorld() method.
  • Create particles effect.  You can change this to different particle type, amount, etc.
  • Uses setDead() to kill the EntityThrowable.

Key Points To Remember About Spawning

The things I would particularly like you to remember are:
  • You can create an Entity instance from the String name using EntityList.createEntityByName() method.
  • Don't forget to set the position of the Entity instance (I often think that my spawning hasn't worked when what has really happened is I forgot to set right position, this is an easy mistake to make).
  • Don't forget to actually put the Entity instance into the world with the worldObj.spawnEntityInWorld() method.

Put Spawn Egg Assets Into Proper Folders

I use the multi-mod workspace structure promoted by LexManos in this tutorial:

In that structure, I put the all my assets in the folder <project_name>/src/main/resources where I create package called assets.<mod_id>.

Texture Assets

Since the spawn egg is an item, I put it in subpackage assets.<mod_id>.textures.items.

In this case, I'm following the way vanilla spawn egg items create a spotted egg so I actually have two textures, one for the egg (spawn_egg.png) and one for the spots (spawn_egg_overlay.png).  I just copied the textures from the vanilla spawn egg.

Set The Display Name For The Spawn Egg Item

I was actually lazy / clever and wanted the display name of the spawn egg item to be generated based on the entity spawned. The problem is that this doesn't allow use of the lang file (which allows for different languages) since the lang file needs pre-set known unlocalized names.  If you were going to publish your mod internationally, you'd probably want to do the work to figure out all the eggs and put in proper lang file entries.

Anyway the way I did it was simply put an @Override for the getItemStackDisplayName() method in the item class that combines the word "Spawn" with the name for the entity to spawn.

    Register Spawn Egg Item

    Because I like having spawn egg created at same time I register an entity, I created a method which does both called registerModEntityWithEgg() which further calls a method for registering the spawn egg item, but if you're just interested in the code for registering the item look at the registerSpawnEgg() method below:

    public void registerModEntity(Class parEntityClass, String parEntityName)
    {
        EntityRegistry.registerModEntity(parEntityClass, parEntityName, ++modEntityID, 
              WildAnimals.instance, 80, 3, false);
    }
    
    public void registerModEntityWithEgg(Class parEntityClass, String parEntityName, 
          int parEggColor, int parEggSpotsColor)
    {
        EntityRegistry.registerModEntity(parEntityClass, parEntityName, ++modEntityID, 
              WildAnimals.instance, 80, 3, false);
        registerSpawnEgg(parEntityName, parEggColor, parEggSpotsColor);
    }
    
    // can't use vanilla spawn eggs with entities registered with modEntityID, so use 
    // custom eggs. Name passed must match entity name string
    public void registerSpawnEgg(String parSpawnName, int parEggColor, int parEggSpotsColor)
    {
        Item itemSpawnEgg = new ItemWildAnimalSpawnEggThrowable(parSpawnName, parEggColor, 
              parEggSpotsColor).setUnlocalizedName("spawn_egg_"+parSpawnName.toLowerCase())
              .setTextureName("wildanimals:spawn_egg");
        GameRegistry.registerItem(itemSpawnEgg, "spawnEgg"+parSpawnName);
    }
    

    No comments:

    Post a Comment