[Intermediate Coding] [1.14] How to Make Custom NPCs Spawn With Custom Trades

Started by rmsandegs on

Topic category: User side tutorials

Joined May 2014
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
[Intermediate Coding] [1.14] How to Make Custom NPCs Spawn With Custom Trades
Sat, 04/11/2020 - 00:49 (edited)

I have had a ton of trouble getting this to work. So, I've decided to post how I did it here so others don't have to share my pain. :P

Heads up, though. This is NOT proper coding but it is far easier than making custom trades the proper way. And, as the title suggests, this does require a VERY basic understanding of Java's structure.

First, we need some setup. First, your custom mob HAS TO extend VillagerEntity (to do this, go to the last page on the entity creator and select Villager from the dropdown menu on the top-right).

Now that that's out of the way, we can crack open the code and inject our super-secret trade juice. To edit code, highlight the entity you want to edit and click the "Edit Code" button on the left menu (It looks like a bunch of lines with a pencil under them) and then click "<Entity Name>.java". It's light-green.

You will now be greeted by a complete mess of "words" and stuff. Don't get flustered, hear me out. On the second line, almost at the top. paste this code:

import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.entity.ILivingEntityData;
import java.util.List;
import java.util.ArrayList;
import net.minecraft.command.ICommandSource;
import net.minecraft.world.IWorld;
import net.minecraft.entity.SpawnReason;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.entity.LivingEntity;
import java.util.Collections;

This will let Java (Minecraft) know what kinds of code we are dealing with. That's really it.

Next, find the section called registerGoals(). Underneath it, we will inject out code. It looks like this. Put your cursor where the circle is in the image and click to move the typing head there. Press the "enter" key and paste this code:

@Override
		public ILivingEntityData onInitialSpawn(IWorld world, DifficultyInstance difficulty, SpawnReason reason, ILivingEntityData livingdata, CompoundNBT tag) 
		{

This will make the rest of our code trigger when the entity is spawned in the world, because we only want it to fire once when the entity is created.

Now we need to set up some variables to hold to custom trades in while the entity is spawning. Hit enter again and paste this code:

ILivingEntityData retval = super.onInitialSpawn(world, difficulty, reason, livingdata, tag);
			int x = (int) this.posX;
			int y = (int) this.posY;
			int z = (int) this.posZ;
			Entity entity = this;
			LivingEntity e = CustomEntity.this;
			int tradeCount = 0;

			String buy1a = "";
			int buy1ad = 0;
			int buy1ac = 0;
			String buy1b = "";
			int buy1bd = 0;
			int buy1bc = 0;
			String sell1 = "";
			int sell1c = 0;

The first few lines are just so Minecraft knows what we're talking about later. All of those "ints" and "Strings" you see are variables that will hold trade data. If you look closely, you'll see some understandable things like buy1a and buy1b, which are what the entity will want in the first and second trade slots respectively, and sell1, which is what the entity will give you in the last trade slot. The other variables, like buy1ad, are for the damage value of the item, and buy1ac is for the number of items required to trade.

All of this is only for 1 trade, though. To make more, copy the code after that blank line and change the names from buy1a, buy1ad, buy1ac, etc. to buy2a, buy2ad, buy2ac, etc. So an entity with 2 trades will look like this: (notice the 3rd section containing new variables)

ILivingEntityData retval = super.onInitialSpawn(world, difficulty, reason, livingdata, tag);
			int x = (int) this.posX;
			int y = (int) this.posY;
			int z = (int) this.posZ;
			Entity entity = this;
			LivingEntity e = CustomEntity.this;
			int tradeCount = 0;

			String buy1a = "";
			int buy1ad = 0;
			int buy1ac = 0;
			String buy1b = "";
			int buy1bd = 0;
			int buy1bc = 0;
			String sell1 = "";
			int sell1c = 0;

			String buy2a = "";
			int buy2ad = 0;
			int buy2ac = 0;
			String buy2b = "";
			int buy2bd = 0;
			int buy2bc = 0;
			String sell2 = "";
			int sell2c = 0;
			int sell2e = 0;

You can make as many trades as you like. In my experience, about 15 feels like enough without feeling like the entity has the same trades every time, which will happen if there aren't enough to choose from.

After this section, paste these few lines. These will put the trades in the villager data:

String tradeA = "";
			int tradeAd = 0;
			int tradeAc = 0;
			String tradeB = "";
			int tradeBd = 0;
			int tradeBc = 0;
			String sell = "";
			int sellC = 0;

The next section is to prevent the entity from having multiple of the same trade when randomly choosing which ones to get when it spawns. Just put it right under the previous stuff:

boolean used1 = false;
			int chosenNum = 0;
			List<Integer> list = new ArrayList<>();

				for (int i = 0; i < 7; i++)
				{
					if (used1 == false)
						list.add(1);

Like in the sections in the past, you have to make new variables if you want more trades. Copy the boolean used1 = false; line and change the number: (used2, used3, etc.). Do this same thing for 

if (used1 == false)
	list.add(1);

So. if you have 3 trades, it will look like this: 

boolean used1 = false;
			boolean used2 = false;
			boolean used3 = false;

			int chosenNum = 0;
			List<Integer> list = new ArrayList<>();

				for (int i = 0; i < 7; i++)
				{
					if (used1 == false)
						list.add(1);
						
					if (used2 == false)
						list.add(2);
						
					if (used3 == false)
						list.add(3);

ALSO, if you look closely, you'll notice this line: for (int i = 0; i < 7; i++). The 7 in that line represents how many trades the entity will have, minus 1. So, because that's a 7, the entity will have 6 trades.

Don't forget to include this line after you're done: Collections.shuffle(list);. You can probably tell what this does by looking at it. It shuffles the numbers around so the trades will be randomized.

Alright. Before we get into the actual trades, let's review. At this point, this is what all of your code should look like if you have 3 trades:

@Override
		public ILivingEntityData onInitialSpawn(IWorld world, DifficultyInstance difficulty, SpawnReason reason, ILivingEntityData livingdata, CompoundNBT tag) 
		{
			ILivingEntityData retval = super.onInitialSpawn(world, difficulty, reason, livingdata, tag);
			int x = (int) this.posX;
			int y = (int) this.posY;
			int z = (int) this.posZ;
			Entity entity = this;
			LivingEntity e = CustomEntity.this;
			int tradeCount = 0;

			String buy1a = "";
			int buy1ad = 0;
			int buy1ac = 0;
			String buy1b = "";
			int buy1bd = 0;
			int buy1bc = 0;
			String sell1 = "";
			int sell1c = 0;

			String tradeA = "";
			int tradeAd = 0;
			int tradeAc = 0;
			String tradeB = "";
			int tradeBd = 0;
			int tradeBc = 0;
			String sell = "";
			int sellC = 0;
			
			boolean used1 = false;
			boolean used2 = false;
			boolean used3 = false;

			int chosenNum = 0;
			List<Integer> list = new ArrayList<>();

				for (int i = 0; i < 4; i++)
				{
					if (used1 == false)
						list.add(1);
						
					if (used2 == false)
						list.add(2);
						
					if (used3 == false)
						list.add(3);

If it doesn't look like this, then you can copy-paste this code and review the previous sections.

This next section requires you to know the ids of items in Minecraft and in your mod. To find the ids of items in Minecraft, simply boot up Minecraft in 1.14 and load into any world where you have access to Creative mode. Then, press the key combo F3 + H (or Ctrl + F3 + H on some laptops) to unlock the ability to view item ids. Now, go into the creative inventory and hover over any item or block you want. At the bottom of the tooltip window, there's the id. For a torch, for example, it looks like this: minecraft:torch. I'd recommend using the test environment so you also get to see the ids of items in your mod, too. They'll look similar, but the minecraft: part of it will be replaced with the name of your mod.

Now that you have access to ids, it's FINALLY time to program the trades! Here's the basic skeleton of what a new trade looks like:

if (list.get(0) == 1)
						{
							tradeA = "";
							tradeAc = [number of items];
							tradeAd = [damage value, type 0 if unsure];
							
							tradeB = "";
							tradeBc = [number of items];
							tradeBd = [damage value, type 0 if unsure];
							
							sell = "";
							sellC = [number of items];
							used1 = true;
						}

See? That's not so bad, right? Between the double quotes ("") is where you put the id of the item you want. Leave the quotes there. Notice the top line, if (list.get(0) == 1). This means that if the code pulls a 1 from the scrambled list of numbers from earlier, it will give the entity this trade. Then, it will set used1 to true so this trade is not given to the entity twice.

As I've been doing, this is an example of three trades, with items included:

if (list.get(0) == 1)
						{
							tradeA = "minecraft:seeds";
							tradeAc = 20;
							tradeAd = 0;

							tradeB = "minecraft:gold_ingot";
							tradeBc = 7;
							tradeBd = 0;
							
							sell = "minecraft:bread";
							sellC = (int) (Math.random() * 3 + 4);
							used1 = true;
						}
						else if (list.get(0) == 2)
						{
							tradeA = "minecraft:emerald";
							tradeAc = 5;
							tradeAd = 0;
							
							tradeB = "minecraft:wheat";
							tradeBc = 10;
							tradeBd = 0;
							
							sell = "minecraft:cake";
							sellC = 1;
							used2 = true;
						}
						else if (list.get(0) == 3)
						{
							tradeA = "minecraft:wheat";
							tradeAc = 6;
							tradeAd = 0;
							
							tradeB = "minecraft:air";
							tradeBc = 1;
							tradeBd = 0;
							
							sell = "minecraft:emerald";
							sellC = 5;
							used3 = true;
						}

Notice the use of minecraft:air in the last trade. If you do this, then the entity will not require anything in that slot to trade. So in this case, the entity ONLY wants 6 wheat and nothing else.

Also notice the use of else if instead of just if. For all trades except the first one, use else if.

After you've written your trades, we're now almost done. Paste these lines underneath your trades:

tradeCount++;
list.removeAll(list);

This code starts selecting the next trade and clears the pool of numbers so it can generate a new random one.

Now, it's time to start writing the trades to the villager's NBT data ( if you don't know what that is, don't worry about it. It's not important :P )

Put this right underneath that last section we wrote:

if (tradeCount == 1) {
						buy1a = tradeA;
						buy1ac = tradeAc;
						buy1ad = tradeAd;
						buy1b = tradeB;
						buy1bc = tradeBc;
						buy1bd = tradeBd;
						sell1 = sell;
						sell1c = sellC;
					}

Notice all the 1s in the text there. This code will make the entity's first trade whatever it decided to randomly choose. For more trades, just copy-paste and change the numbers around. Standard procedure at this point: (this code is for 3 trades)

if (tradeCount == 1) {
						buy1a = tradeA;
						buy1ac = tradeAc;
						buy1ad = tradeAd;
						buy1b = tradeB;
						buy1bc = tradeBc;
						buy1bd = tradeBd;
						sell1 = sell;
						sell1c = sellC;
					}
					else if (tradeCount == 2) {
						buy2a = tradeA;
						buy2ac = tradeAc;
						buy2ad = tradeAd;
						buy2b = tradeB;
						buy2bc = tradeBc;
						buy2bd = tradeBd;
						sell2 = sell;
						sell2c = sellC;
					}
					else if (tradeCount == 3) {
						buy3a = tradeA;
						buy3ac = tradeAc;
						buy3ad = tradeAd;
						buy3b = tradeB;
						buy3bc = tradeBc;
						buy3bd = tradeBd;
						sell3 = sell;
						sell3c = sellC;
					}

Like in the section a little bit ago, the use of else if is important, but make sure to just use if in the first section.

At the end, press enter to go one line down and type a single }.

Now, to wrap up, we put the trades in the villager by executing a command. It looks like this:

entity.world.getServer().getCommandManager().handleCommand(entity.getCommandSource().withFeedbackDisabled().withPermissionLevel(4),
					"data merge entity @s {VillagerData:{profession:farmer,level:2,type:plains},Offers:{Recipes:[" + 
							"{buy:{id:" + "\"" + buy1a + "\"" + ",Count:" + buy1ac + ",Damage:" + buy1ad + "}," + 
							"buyB:{id:" + "\"" + buy1b + "\"" + ",Count:" + buy1bc + ",Damage:"+ buy1bd + "}," + 
							"sell:{id:" + "\"" + sell1 + "\"" + ",Count:" + sell1c + "},maxUses:9999999}," + "]}}");

Now,  know what you're thinking: Holy mackerel! This looks absolutely insane! And yeah, it does look messy, but there is a method to the madness.

If you look, we are using the /data merge command to write data to the entity when it spawns. The VillagerData part of it contains basic information like the level, profession, and type of "villager", all of which rarely matters. It's just required for the trades to work.

A couple lines down, you see we make reference to our variables from the beginning (buy1a, buy1b, sell1). We are taking the variables from our code and putting them into the entity, that's it. For 3 trades, this is what the code will look like. Notice how it's just more of the same, just repeated and slightly changed:

entity.world.getServer().getCommandManager().handleCommand(entity.getCommandSource().withFeedbackDisabled().withPermissionLevel(4),
					"data merge entity @s {VillagerData:{profession:farmer,level:2,type:plains}," + 
							"Offers:{Recipes:[" + 
							"{buy:{id:" + "\"" + buy1a + "\"" + ",Count:" + buy1ac + ",Damage:" + buy1ad + "}," + 
							"buyB:{id:" + "\"" + buy1b + "\"" + ",Count:" + buy1bc + ",Damage:"+ buy1bd + "}," + 
							"sell:{id:" + "\"" + sell1 + "\"" + ",Count:" + sell1c + "},maxUses:9999999}," + 
							"{buy:{id:" + "\"" + buy2a + "\"" + ",Count:" + buy2ac + ",Damage:" + buy2ad + "}," + 
							"buyB:{id:" + "\"" + buy2b + "\"" + ",Count:" + buy2bc + ",Damage:" + buy2bd + "}," + 
							"sell:{id:" + "\"" + sell2 + "\"" + ",Count:" + sell2c + "},maxUses:9999999}," + 
							"{buy:{id:" + "\"" + buy3a + "\"" + ",Count:" + buy3ac + ",Damage:" + buy3ad + "}," + 
							"buyB:{id:" + "\"" + buy3b + "\"" + ",Count:" + buy3bc + ",Damage:" + buy3bd + "}," + 
							"sell:{id:" + "\"" + sell3 + "\"" + ",Count:" + sell3c + "},maxUses:9999999}," + "]}}");

You see that mess of ]}}"};? That only goes on the very end and is not repeated like everything else.

If you want more trades, copy the three lines starting at ..."buy:{... and ending at ...maxUses:9999999},... and change the varable numbers (buy1a, buy1b, sell1).

DO NOT CHANGE THE VARIABLES AT THE FAR LEFT (buy, buyB, sell).

We're now on the home stretch! just paste these final lines to complete the code:

return retval;
		}

And with that, YOU ARE NOW DONE! WOOHOO! For reference, here's the complete code of 3 trades:

@Override
		public ILivingEntityData onInitialSpawn(IWorld world, DifficultyInstance difficulty, SpawnReason reason, ILivingEntityData livingdata, CompoundNBT tag) 
		{
			ILivingEntityData retval = super.onInitialSpawn(world, difficulty, reason, livingdata, tag);
			int x = (int) this.posX;
			int y = (int) this.posY;
			int z = (int) this.posZ;
			Entity entity = this;
			LivingEntity e = CustomEntity.this;
			int tradeCount = 0;

			String buy1a = "";
			int buy1ad = 0;
			int buy1ac = 0;
			String buy1b = "";
			int buy1bd = 0;
			int buy1bc = 0;
			String sell1 = "";
			int sell1c = 0;
			int sell1e = 0;
			
			String buy2a = "";
			int buy2ad = 0;
			int buy2ac = 0;
			String buy2b = "";
			int buy2bd = 0;
			int buy2bc = 0;
			String sell2 = "";
			int sell2c = 0;
			int sell2e = 0;
			
			String buy3a = "";
			int buy3ad = 0;
			int buy3ac = 0;
			String buy3b = "";
			int buy3bd = 0;
			int buy3bc = 0;
			String sell3 = "";
			int sell3c = 0;
			int sell3e = 0;
			
			String tradeA = "";
			int tradeAd = 0;
			int tradeAc = 0;
			String tradeB = "";
			int tradeBd = 0;
			int tradeBc = 0;
			String sell = "";
			int sellC = 0;
			String sellE = "";
			
			boolean used1 = false;
			boolean used2 = false;
			boolean used3 = false;
			int chosenNum = 0;
			List<Integer> list = new ArrayList<>();

				for (int i = 0; i < 7; i++)
				{
					if (used1 == false)
						list.add(1);
						
					if (used2 == false)
						list.add(2);
						
					if (used3 == false)
						list.add(3);
					
					Collections.shuffle(list);

						if (list.get(0) == 1)
						{
							tradeA = "minecraft:seeds";
							tradeAc = 5
							tradeAd = 0;
							
							tradeB = "minecraft:ender_pearl";
							tradeBc = 1;
							tradeBd = 0;
							
							sell = "minecraft:bread";
							sellC = 6;
							used1 = true;
						}
						else if (list.get(0) == 2)
						{
							tradeA = "minecraft:emerald";
							tradeAc = 1;
							tradeAd = 0;
							
							tradeB = "minecraft:wheat";
							tradeBc = 6;
							tradeBd = 0;
							
							sell = "minecraft:cake";
							sellC = 1;
							used2 = true;
						}
						else if (list.get(0) == 3)
						{
							tradeA = "minecraft:wheat";
							tradeAc = 1;
							tradeAd = 0;
							
							tradeB = "minecraft:air";
							tradeBc = 1;
							tradeBd = 0;
							
							sell = "minecraft:oak_log";
							sellC = 5;
							used3 = true;
						}
		
					tradeCount++;
					list.removeAll(list);
		
					if (tradeCount == 1) {
						buy1a = tradeA;
						buy1ac = tradeAc;
						buy1ad = tradeAd;
						buy1b = tradeB;
						buy1bc = tradeBc;
						buy1bd = tradeBd;
						sell1 = sell;
						sell1c = sellC;
					}
					else if (tradeCount == 2) {
						buy2a = tradeA;
						buy2ac = tradeAc;
						buy2ad = tradeAd;
						buy2b = tradeB;
						buy2bc = tradeBc;
						buy2bd = tradeBd;
						sell2 = sell;
						sell2c = sellC;
					}
					else if (tradeCount == 3) {
						buy3a = tradeA;
						buy3ac = tradeAc;
						buy3ad = tradeAd;
						buy3b = tradeB;
						buy3bc = tradeBc;
						buy3bd = tradeBd;
						sell3 = sell;
						sell3c = sellC;
					}
				}
		
				entity.world.getServer().getCommandManager().handleCommand(entity.getCommandSource().withFeedbackDisabled().withPermissionLevel(4),
					"data merge entity @s {VillagerData:{profession:farmer,level:2,type:plains}," + 
							"Offers:{Recipes:[" + 
							"{buy:{id:" + "\"" + buy1a + "\"" + ",Count:" + buy1ac + ",Damage:" + buy1ad + "}," + 
							"buyB:{id:" + "\"" + buy1b + "\"" + ",Count:" + buy1bc + ",Damage:"+ buy1bd + "}," + 
							"sell:{id:" + "\"" + sell1 + "\"" + ",Count:" + sell1c + "},maxUses:9999999}," + 
							"{buy:{id:" + "\"" + buy2a + "\"" + ",Count:" + buy2ac + ",Damage:" + buy2ad + "}," + 
							"buyB:{id:" + "\"" + buy2b + "\"" + ",Count:" + buy2bc + ",Damage:" + buy2bd + "}," + 
							"sell:{id:" + "\"" + sell2 + "\"" + ",Count:" + sell2c + "},maxUses:9999999}," + 
							"{buy:{id:" + "\"" + buy3a + "\"" + ",Count:" + buy3ac + ",Damage:" + buy3ad + "}," + 
							"buyB:{id:" + "\"" + buy3b + "\"" + ",Count:" + buy3bc + ",Damage:" + buy3bd + "}," + 
							"sell:{id:" + "\"" + sell3 + "\"" + ",Count:" + sell3c + "},maxUses:9999999}," + "]}}");
			return retval;
		}

I hope this was easy to follow. This was only my second tutorial I've made, so I'm still not great at explaining. This also took me about 2 hours to write, so I'm sure there are problems. If you have questions or something is wrong, feel free to comment!

Edited by rmsandegs on Sat, 04/11/2020 - 00:49
Joined Mar 2020
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
Okay I just did.
Mon, 05/25/2020 - 03:04

Okay I just did.

Joined May 2014
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
Yes, you're welcome. Now…
Mon, 05/25/2020 - 03:37

Yes, you're welcome. Now please stop posting so many comments. Just one is enough to say what you want to say.

Joined Mar 2020
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
I think all your supposed to…
Thu, 05/28/2020 - 14:33

I think all your supposed to do is just make a GUI.

Joined Mar 2021
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
  I guess it doesn't work…
Thu, 04/15/2021 - 21:42

 

I guess it doesn't work for version 1.16.5... Are you going to make a new tutorial?