Minecraft Forge Modding: Villagers

Background


Villagers are a special passive entity in Minecraft that have complex interactions and AI behavior. In particular, they can be used for trading which is a common thing that people may want to mod.

Adding Trades To Vanilla Professions


If you want to add trades to existing villager professions, you need to:
  1. Create trade list: Implement EntityVillager.ITradeList class and @Override the addMerchantRecipe() method to call the recipeList.add() method to add your additional trades.
  2. Get the profession instance: In the init loading phase (not pre-init because professions are registered after pre-init) you can get the profession from ForgeRegistries.VILLAGER_PROFESSIONS.getValue() method. Note that the parameter is a ResourceLocation such as "minecraft:nitwit". Inspect the vanilla classes to get the other registry references for other professions.
  3. Get the career instance: Using the profession instance from Step #2, the career instance can be retrieved with the getCareer() method. Note that the parameter is an int or career name string, and you can look up the career ID in the villager careeer official wiki.
  4. Add your trade list to the career instance: Use the addTrade() method. The first int parameter is the level of the trade to add.

Creating Custom Villager Professions and Careers


Professions have multiple careers and each career has trades and AI. You can learn more at the villager careeer official wiki.

To create a new profession with careers and trades you need to:
  1. Declare an @ObjectHolder profession instance: You should declare a public static final VillagerProfession field that you assign to null in order to set it up for the @ObjectHolder method (where you initialize it to null then get it injected by registering (see Step #2) with a field name that matches the registry name. See example code.
  2. Register the profession instance: You need to handle the RegistryEvent.Register event and use the event.getRegistry().register() method to register a new VillageProfession instance that has a matching profession registry name as well as locations for the regular and zombie textures. Warning: The string values passed for the textures should contain your modid with colon and then full asset path (e.g. "examplemod:textures/entities/my_texture.png").  See example code.
  3. Create the career instances. In your init loading phase (not pre-init because profession registry event happens after pre-init) you can simply make new VillagerCareer, where you pass a profession instance and career name. Note that the constructor will take care of registering the career with the profession. See example code.
  4. Add trades to the career instances: Call the addTrade() method on the career instances, passing a trades list and associating it with the career. You can use the vanilla ITradeList implementations or create your own. The addTrade() method can be chained directly to the career instance creation. See example code.
  5. Create texture assets for the profession. You need to ensure you have a PNG image texture asset in the location you indicated when you registered the profession. You need one for the normal villager and one for the zombie version.
  6. Add the careers to your .lang files. When you open trades with your villager, the career name is printed at the top of the GUI. The unlocalized version will be "entity.Villager." plus the career name you used when you created it. So you need to create a localized version of the name in your .lang file. See example code.

Changing Villager AI


If you want to change the AI of vanilla villagers, you can do that by directly replacing the AI tasks. For all vanilla mobs, the AI task lists are public scope so you are free to change them including clearing them completely and adding your own. See Jabelar's Custom Entity AI Tutorial.

6 comments:

  1. I cant get profession. It returns null and I get null pointer exception. I try to get profession in public static function scope:

    public static void addTradeToVillager(Trade trade, String profession, int career, int level)
    {
    VillagerProfession professionInstance = ForgeRegistries.VILLAGER_PROFESSIONS.getValue(new ResourceLocation(profession));
    professionInstance.getCareer(career).addTrade(level, trade);
    }

    Trade is that thing that implements ITradeList

    ReplyDelete
  2. I want to make my own mob that functions like a villager. Any Ideas?

    ReplyDelete
  3. Is this out of date for 1.16?

    ReplyDelete
  4. I had this error as well and I just had to add the @ObjectHolder declaration

    ReplyDelete