diff --git a/patches/minecraft/net/minecraft/entity/NpcMerchant.java.patch b/patches/minecraft/net/minecraft/entity/NpcMerchant.java.patch
new file mode 100644
index 000000000..2b48c073d
--- /dev/null
+++ b/patches/minecraft/net/minecraft/entity/NpcMerchant.java.patch
@@ -0,0 +1,11 @@
+--- a/net/minecraft/entity/NpcMerchant.java
++++ b/net/minecraft/entity/NpcMerchant.java
+@@ -36,7 +36,7 @@
+
+ @Nullable
+ public MerchantRecipeList func_70934_b(EntityPlayer p_70934_1_) {
+- return this.field_70936_c;
++ return net.minecraftforge.event.ForgeEventFactory.listTradeOffers(this, p_70934_1_, this.field_70936_c);
+ }
+
+ public void func_70930_a(@Nullable MerchantRecipeList p_70930_1_) {
diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityVillager.java.patch b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.java.patch
index fc87d7c1c..e19504b5e 100644
--- a/patches/minecraft/net/minecraft/entity/passive/EntityVillager.java.patch
+++ b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.java.patch
@@ -121,7 +121,12 @@
} else {
this.field_82189_bL = null;
}
-@@ -473,11 +512,10 @@
+@@ -469,15 +508,14 @@
+ this.func_175554_cu();
+ }
+
+- return this.field_70963_i;
++ return net.minecraftforge.event.ForgeEventFactory.listTradeOffers(this, p_70934_1_, field_70963_i);
}
private void func_175554_cu() {
diff --git a/patches/minecraft/net/minecraft/village/VillageSiege.java.patch b/patches/minecraft/net/minecraft/village/VillageSiege.java.patch
new file mode 100644
index 000000000..98a6c8ff9
--- /dev/null
+++ b/patches/minecraft/net/minecraft/village/VillageSiege.java.patch
@@ -0,0 +1,10 @@
+--- a/net/minecraft/village/VillageSiege.java
++++ b/net/minecraft/village/VillageSiege.java
+@@ -115,6 +115,7 @@
+
+ Vec3d vec3d = this.func_179867_a(new BlockPos(this.field_75532_g, this.field_75538_h, this.field_75539_i));
+ if (vec3d != null) {
++ if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.village.VillageSiegeEvent(this, field_75537_a, entityplayer, field_75531_f, vec3d))) return false;
+ break;
+ }
+ }
diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java
index 2e8cc266d..a6af63ec4 100644
--- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java
+++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java
@@ -35,6 +35,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.EnumCreatureType;
+import net.minecraft.entity.IMerchant;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.monster.EntityZombie;
@@ -62,6 +63,8 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.ChatType;
import net.minecraft.util.text.ITextComponent;
+import net.minecraft.village.MerchantRecipeList;
+import net.minecraft.village.Village;
import net.minecraft.world.Explosion;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
@@ -118,6 +121,7 @@ import net.minecraftforge.event.entity.player.SleepingTimeCheckEvent;
import net.minecraftforge.event.entity.player.UseHoeEvent;
import net.minecraftforge.event.furnace.FurnaceFuelBurnTimeEvent;
import net.minecraftforge.event.terraingen.ChunkGeneratorEvent;
+import net.minecraftforge.event.village.MerchantTradeOffersEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.BlockEvent.CreateFluidSourceEvent;
import net.minecraftforge.event.world.BlockEvent.EntityMultiPlaceEvent;
@@ -695,4 +699,17 @@ public class ForgeEventFactory
MinecraftForge.EVENT_BUS.post(event);
return event.getResult() != Result.DENY;
}
+
+ public static MerchantRecipeList listTradeOffers(IMerchant merchant, EntityPlayer player, @Nullable MerchantRecipeList list)
+ {
+ MerchantRecipeList dupeList = null;
+ if (list != null)
+ {
+ dupeList = new MerchantRecipeList();
+ dupeList.addAll(list);
+ }
+ MerchantTradeOffersEvent event = new MerchantTradeOffersEvent(merchant, player, dupeList);
+ MinecraftForge.EVENT_BUS.post(event);
+ return event.getList();
+ }
}
diff --git a/src/main/java/net/minecraftforge/event/village/MerchantTradeOffersEvent.java b/src/main/java/net/minecraftforge/event/village/MerchantTradeOffersEvent.java
new file mode 100644
index 000000000..474c38cde
--- /dev/null
+++ b/src/main/java/net/minecraftforge/event/village/MerchantTradeOffersEvent.java
@@ -0,0 +1,80 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2018.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.event.village;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.IMerchant;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.village.MerchantRecipeList;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.eventbus.api.Cancelable;
+import net.minecraftforge.eventbus.api.Event;
+
+/**
+ * MerchantTradeOffersEvent is fired when a list of villager trade offers is presented in
+ * {@link IMerchant#getRecipes(EntityPlayer)}, allowing mods to modify trade offers depending
+ * on the player. Be warned that this event is fired on both server and client; thus, modders
+ * should ensure that they sync the needed data for this event themselves.
+ *
+ * This event is not {@link Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ */
+public class MerchantTradeOffersEvent extends Event
+{
+ private final IMerchant merchant;
+ private final EntityPlayer player;
+ private @Nullable MerchantRecipeList list;
+
+ public MerchantTradeOffersEvent(IMerchant merchant, EntityPlayer player, @Nullable MerchantRecipeList list)
+ {
+ this.merchant = merchant;
+ this.player = player;
+ this.list = list;
+ }
+
+ /**
+ * The recipe list (if not {@code null}) returned from this function may be modified.
+ * @return the recipe list
+ */
+ public @Nullable MerchantRecipeList getList()
+ {
+ return list;
+ }
+
+ public void setList(@Nullable MerchantRecipeList list)
+ {
+ this.list = list;
+ }
+
+ public IMerchant getMerchant()
+ {
+ return merchant;
+ }
+
+ public EntityPlayer getPlayer()
+ {
+ return player;
+ }
+
+}
diff --git a/src/main/java/net/minecraftforge/event/village/VillageSiegeEvent.java b/src/main/java/net/minecraftforge/event/village/VillageSiegeEvent.java
new file mode 100644
index 000000000..aa4ea6972
--- /dev/null
+++ b/src/main/java/net/minecraftforge/event/village/VillageSiegeEvent.java
@@ -0,0 +1,83 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2018.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.event.village;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.village.Village;
+import net.minecraft.village.VillageSiege;
+import net.minecraft.world.World;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.eventbus.api.Cancelable;
+import net.minecraftforge.eventbus.api.Event;
+
+/**
+ * VillageSiegeEvent is fired just before a zombie siege finds a successful location in
+ * {@link VillageSiege#trySetupSiege}, to give mods the chance to stop the siege.
+ *
+ * This event is {@link Cancelable}; canceling stops the siege.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ */
+@Cancelable
+public class VillageSiegeEvent extends Event
+{
+ private final VillageSiege siege;
+ private final World world;
+ private final EntityPlayer player;
+ private final Village village;
+ private final Vec3d attemptedSpawnPos;
+
+ public VillageSiegeEvent(VillageSiege siege, World world, EntityPlayer player, Village village, Vec3d attemptedSpawnPos)
+ {
+ this.siege = siege;
+ this.world = world;
+ this.player = player;
+ this.village = village;
+ this.attemptedSpawnPos = attemptedSpawnPos;
+ }
+
+ public VillageSiege getSiege()
+ {
+ return siege;
+ }
+
+ public World getWorld()
+ {
+ return world;
+ }
+
+ public EntityPlayer getPlayer()
+ {
+ return player;
+ }
+
+ public Village getVillage()
+ {
+ return village;
+ }
+
+ public Vec3d getAttemptedSpawnPos()
+ {
+ return attemptedSpawnPos;
+ }
+}
diff --git a/src/test/java/net/minecraftforge/debug/village/MerchantTradeOffersEventTest.java b/src/test/java/net/minecraftforge/debug/village/MerchantTradeOffersEventTest.java
new file mode 100644
index 000000000..330a3dc47
--- /dev/null
+++ b/src/test/java/net/minecraftforge/debug/village/MerchantTradeOffersEventTest.java
@@ -0,0 +1,66 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2018.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.debug.village;
+
+import java.util.ListIterator;
+
+import net.minecraft.village.MerchantRecipe;
+import net.minecraft.village.MerchantRecipeList;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.village.MerchantTradeOffersEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+/**
+ * Tests {@link MerchantTradeOffersEvent}. When enabled, the item that the villager sells to
+ * the player will be maxed out in stack size.
+ */
+@Mod(modid = MerchantTradeOffersEventTest.MODID, name = MerchantTradeOffersEventTest.NAME, version = "0.0.0", acceptableRemoteVersions = "*")
+public class MerchantTradeOffersEventTest
+{
+ public static final String MODID = "merchanttradeofferseventtest";
+ public static final String NAME = "Merchant Trade Offers Event Test";
+ public static final boolean ENABLED = false;
+
+ @Mod.EventHandler
+ public void preInit(FMLPreInitializationEvent event)
+ {
+ if (ENABLED)
+ {
+ MinecraftForge.EVENT_BUS.register(MerchantTradeOffersEventTest.class);
+ }
+ }
+
+ @SubscribeEvent
+ public static void onGetRecipes(MerchantTradeOffersEvent event)
+ {
+ MerchantRecipeList list = event.getList();
+ if (list != null)
+ {
+ ListIterator it = list.listIterator();
+ while (it.hasNext())
+ {
+ MerchantRecipe recipe = it.next();
+ recipe.getItemToSell().setCount(recipe.getItemToSell().getMaxStackSize());
+ }
+ }
+ }
+}
diff --git a/src/test/java/net/minecraftforge/debug/village/VillageSiegeEventTest.java b/src/test/java/net/minecraftforge/debug/village/VillageSiegeEventTest.java
new file mode 100644
index 000000000..bbf23c037
--- /dev/null
+++ b/src/test/java/net/minecraftforge/debug/village/VillageSiegeEventTest.java
@@ -0,0 +1,65 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2018.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.debug.village;
+
+import java.util.Locale;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.init.Items;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.village.VillageSiegeEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+/**
+ * Tests {@link VillageSiegeEvent}. When enabled, players holding a diamond sword in the mainhand
+ * slot are not counted in determining where the zombie siege occurs.
+ */
+@Mod(modid = VillageSiegeEventTest.MODID, name = VillageSiegeEventTest.NAME, version = "0.0.0", acceptableRemoteVersions = "*")
+public class VillageSiegeEventTest
+{
+ public static final String MODID = "villagesiegeeventtest";
+ public static final String NAME = "Village Siege Event Test";
+ public static final boolean ENABLED = false;
+
+ public static final Logger LOG = LogManager.getLogger(MODID.toUpperCase(Locale.US));
+
+ @Mod.EventHandler
+ public void preInit(FMLPreInitializationEvent event)
+ {
+ if (ENABLED)
+ {
+ MinecraftForge.EVENT_BUS.register(VillageSiegeEventTest.class);
+ }
+ }
+
+ @SubscribeEvent
+ public static void onVillageSiege(VillageSiegeEvent event)
+ {
+ if (!event.getWorld().isRemote && event.getPlayer().getHeldItemMainhand().getItem() == Items.DIAMOND_SWORD)
+ {
+ LOG.info("Village siege event for player "+event.getPlayer().getName()+" canceled");
+ event.setCanceled(true);
+ }
+ }
+}