diff --git a/patches/minecraft/net/minecraft/entity/EntityLivingBase.java.patch b/patches/minecraft/net/minecraft/entity/EntityLivingBase.java.patch
index 7d5caa9c76..14d681529c 100644
--- a/patches/minecraft/net/minecraft/entity/EntityLivingBase.java.patch
+++ b/patches/minecraft/net/minecraft/entity/EntityLivingBase.java.patch
@@ -177,7 +177,7 @@
this.func_184185_a(soundtype.func_185842_g(), soundtype.func_185843_a() * 0.5F, soundtype.func_185847_b() * 0.75F);
}
}
-@@ -1380,6 +1395,8 @@
+@@ -1380,17 +1395,20 @@
{
if (!this.func_180431_b(p_70665_1_))
{
@@ -186,7 +186,10 @@
p_70665_2_ = this.func_70655_b(p_70665_1_, p_70665_2_);
p_70665_2_ = this.func_70672_c(p_70665_1_, p_70665_2_);
float f = p_70665_2_;
-@@ -1389,8 +1406,8 @@
+ p_70665_2_ = Math.max(p_70665_2_ - this.func_110139_bj(), 0.0F);
+ this.func_110149_m(this.func_110139_bj() - (f - p_70665_2_));
++ p_70665_2_ = net.minecraftforge.common.ForgeHooks.onLivingDamage(this, p_70665_1_, p_70665_2_);
+
if (p_70665_2_ != 0.0F)
{
float f1 = this.func_110143_aJ();
@@ -196,7 +199,7 @@
this.func_110149_m(this.func_110139_bj() - p_70665_2_);
}
}
-@@ -1447,6 +1464,11 @@
+@@ -1447,6 +1465,11 @@
public void func_184609_a(EnumHand p_184609_1_)
{
@@ -208,7 +211,7 @@
if (!this.field_82175_bq || this.field_110158_av >= this.func_82166_i() / 2 || this.field_110158_av < 0)
{
this.field_110158_av = -1;
-@@ -1694,7 +1716,7 @@
+@@ -1694,7 +1717,7 @@
if (!this.field_70170_p.func_184143_b(axisalignedbb1))
{
@@ -217,7 +220,7 @@
{
this.func_70634_a(d11, this.field_70163_u + 1.0D, d12);
return;
-@@ -1702,14 +1724,14 @@
+@@ -1702,14 +1725,14 @@
BlockPos blockpos = new BlockPos(d11, this.field_70163_u - 1.0D, d12);
@@ -234,7 +237,7 @@
{
d1 = d11;
d13 = this.field_70163_u + 2.0D;
-@@ -1781,6 +1803,7 @@
+@@ -1781,6 +1804,7 @@
}
this.field_70160_al = true;
@@ -242,7 +245,7 @@
}
protected void func_70629_bd()
-@@ -1874,7 +1897,8 @@
+@@ -1874,7 +1898,8 @@
if (this.field_70122_E)
{
@@ -252,7 +255,7 @@
}
float f7 = 0.16277136F / (f6 * f6 * f6);
-@@ -1894,7 +1918,8 @@
+@@ -1894,7 +1919,8 @@
if (this.field_70122_E)
{
@@ -262,7 +265,7 @@
}
if (this.func_70617_f_())
-@@ -2054,6 +2079,7 @@
+@@ -2054,6 +2080,7 @@
public void func_70071_h_()
{
@@ -270,7 +273,7 @@
super.func_70071_h_();
this.func_184608_ct();
-@@ -2096,7 +2122,9 @@
+@@ -2096,7 +2123,9 @@
if (!ItemStack.func_77989_b(itemstack1, itemstack))
{
@@ -280,7 +283,7 @@
if (!itemstack.func_190926_b())
{
-@@ -2575,6 +2603,40 @@
+@@ -2575,6 +2604,40 @@
this.field_70752_e = true;
}
@@ -321,7 +324,7 @@
public abstract EnumHandSide func_184591_cq();
public boolean func_184587_cr()
-@@ -2595,12 +2657,19 @@
+@@ -2595,12 +2658,19 @@
if (itemstack == this.field_184627_bm)
{
@@ -342,7 +345,7 @@
{
this.func_71036_o();
}
-@@ -2618,8 +2687,10 @@
+@@ -2618,8 +2688,10 @@
if (!itemstack.func_190926_b() && !this.func_184587_cr())
{
@@ -354,7 +357,7 @@
if (!this.field_70170_p.field_72995_K)
{
-@@ -2700,7 +2771,9 @@
+@@ -2700,7 +2772,9 @@
if (!this.field_184627_bm.func_190926_b() && this.func_184587_cr())
{
this.func_184584_a(this.field_184627_bm, 16);
@@ -365,7 +368,7 @@
this.func_184602_cy();
}
}
-@@ -2724,7 +2797,8 @@
+@@ -2724,7 +2798,8 @@
{
if (!this.field_184627_bm.func_190926_b())
{
@@ -375,7 +378,7 @@
}
this.func_184602_cy();
-@@ -2852,6 +2926,31 @@
+@@ -2852,6 +2927,31 @@
return true;
}
diff --git a/patches/minecraft/net/minecraft/entity/player/EntityPlayer.java.patch b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.java.patch
index 7abbbab2cc..9f62c6b2be 100644
--- a/patches/minecraft/net/minecraft/entity/player/EntityPlayer.java.patch
+++ b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.java.patch
@@ -233,7 +233,7 @@
if (enumhand == EnumHand.MAIN_HAND)
{
-@@ -1045,7 +1117,10 @@
+@@ -1045,11 +1117,15 @@
{
if (!this.func_180431_b(p_70665_1_))
{
@@ -245,7 +245,12 @@
p_70665_2_ = this.func_70672_c(p_70665_1_, p_70665_2_);
float f = p_70665_2_;
p_70665_2_ = Math.max(p_70665_2_ - this.func_110139_bj(), 0.0F);
-@@ -1115,6 +1190,8 @@
+ this.func_110149_m(this.func_110139_bj() - (f - p_70665_2_));
++ p_70665_2_ = net.minecraftforge.common.ForgeHooks.onLivingDamage(this, p_70665_1_, p_70665_2_);
+
+ if (p_70665_2_ != 0.0F)
+ {
+@@ -1115,6 +1191,8 @@
}
else
{
@@ -254,7 +259,7 @@
ItemStack itemstack = this.func_184586_b(p_190775_2_);
ItemStack itemstack1 = itemstack.func_190926_b() ? ItemStack.field_190927_a : itemstack.func_77946_l();
-@@ -1124,7 +1201,10 @@
+@@ -1124,7 +1202,10 @@
{
itemstack.func_190920_e(itemstack1.func_190916_E());
}
@@ -266,7 +271,7 @@
return EnumActionResult.SUCCESS;
}
else
-@@ -1140,6 +1220,7 @@
+@@ -1140,6 +1221,7 @@
{
if (itemstack.func_190926_b() && !this.field_71075_bZ.field_75098_d)
{
@@ -274,7 +279,7 @@
this.func_184611_a(p_190775_2_, ItemStack.field_190927_a);
}
-@@ -1165,6 +1246,7 @@
+@@ -1165,6 +1247,7 @@
public void func_71059_n(Entity p_71059_1_)
{
@@ -282,7 +287,7 @@
if (p_71059_1_.func_70075_an())
{
if (!p_71059_1_.func_85031_j(this))
-@@ -1203,9 +1285,11 @@
+@@ -1203,9 +1286,11 @@
boolean flag2 = flag && this.field_70143_R > 0.0F && !this.field_70122_E && !this.func_70617_f_() && !this.func_70090_H() && !this.func_70644_a(MobEffects.field_76440_q) && !this.func_184218_aH() && p_71059_1_ instanceof EntityLivingBase;
flag2 = flag2 && !this.func_70051_ag();
@@ -295,7 +300,7 @@
}
f = f + f1;
-@@ -1332,10 +1416,12 @@
+@@ -1332,10 +1417,12 @@
if (!itemstack1.func_190926_b() && entity instanceof EntityLivingBase)
{
@@ -308,7 +313,7 @@
this.func_184611_a(EnumHand.MAIN_HAND, ItemStack.field_190927_a);
}
}
-@@ -1384,7 +1470,7 @@
+@@ -1384,7 +1471,7 @@
if (this.field_70146_Z.nextFloat() < f)
{
@@ -317,7 +322,7 @@
this.func_184602_cy();
this.field_70170_p.func_72960_a(this, (byte)30);
}
-@@ -1442,6 +1528,8 @@
+@@ -1442,6 +1529,8 @@
public EntityPlayer.SleepResult func_180469_a(BlockPos p_180469_1_)
{
@@ -326,7 +331,7 @@
EnumFacing enumfacing = (EnumFacing)this.field_70170_p.func_180495_p(p_180469_1_).func_177229_b(BlockHorizontal.field_185512_D);
if (!this.field_70170_p.field_72995_K)
-@@ -1484,8 +1572,9 @@
+@@ -1484,8 +1573,9 @@
this.func_192030_dh();
this.func_70105_a(0.2F, 0.2F);
@@ -338,7 +343,7 @@
float f1 = 0.5F + (float)enumfacing.func_82601_c() * 0.4F;
float f = 0.5F + (float)enumfacing.func_82599_e() * 0.4F;
this.func_175139_a(enumfacing);
-@@ -1532,13 +1621,14 @@
+@@ -1532,13 +1622,14 @@
public void func_70999_a(boolean p_70999_1_, boolean p_70999_2_, boolean p_70999_3_)
{
@@ -356,7 +361,7 @@
if (blockpos == null)
{
-@@ -1547,6 +1637,10 @@
+@@ -1547,6 +1638,10 @@
this.func_70107_b((double)((float)blockpos.func_177958_n() + 0.5F), (double)((float)blockpos.func_177956_o() + 0.1F), (double)((float)blockpos.func_177952_p() + 0.5F));
}
@@ -367,7 +372,7 @@
this.field_71083_bS = false;
-@@ -1565,15 +1659,16 @@
+@@ -1565,15 +1660,16 @@
private boolean func_175143_p()
{
@@ -387,7 +392,7 @@
{
if (!p_180467_2_)
{
-@@ -1588,16 +1683,17 @@
+@@ -1588,16 +1684,17 @@
}
else
{
@@ -408,7 +413,7 @@
switch (enumfacing)
{
-@@ -1637,16 +1733,24 @@
+@@ -1637,16 +1734,24 @@
public BlockPos func_180470_cg()
{
@@ -435,7 +440,7 @@
if (p_180473_1_ != null)
{
this.field_71077_c = p_180473_1_;
-@@ -1839,6 +1943,10 @@
+@@ -1839,6 +1944,10 @@
super.func_180430_e(p_180430_1_, p_180430_2_);
}
@@ -446,7 +451,7 @@
}
protected void func_71061_d_()
-@@ -2176,7 +2284,10 @@
+@@ -2176,7 +2285,10 @@
public ITextComponent func_145748_c_()
{
@@ -458,7 +463,7 @@
itextcomponent.func_150256_b().func_150241_a(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/msg " + this.func_70005_c_() + " "));
itextcomponent.func_150256_b().func_150209_a(this.func_174823_aP());
itextcomponent.func_150256_b().func_179989_a(this.func_70005_c_());
-@@ -2185,7 +2296,7 @@
+@@ -2185,7 +2297,7 @@
public float func_70047_e()
{
@@ -467,7 +472,7 @@
if (this.func_70608_bn())
{
-@@ -2421,6 +2532,168 @@
+@@ -2421,6 +2533,168 @@
return this.field_71075_bZ.field_75098_d && this.func_70003_b(2, "");
}
diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java
index 1ca656a10d..3b09e21a7b 100644
--- a/src/main/java/net/minecraftforge/common/ForgeHooks.java
+++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java
@@ -113,6 +113,7 @@ import net.minecraftforge.event.ServerChatEvent;
import net.minecraftforge.event.entity.EntityTravelToDimensionEvent;
import net.minecraftforge.event.entity.item.ItemTossEvent;
import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.event.entity.living.LivingDamageEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.entity.living.LivingEvent.LivingJumpEvent;
@@ -564,6 +565,12 @@ public class ForgeHooks
return (MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount());
}
+ public static float onLivingDamage(EntityLivingBase entity, DamageSource src, float amount)
+ {
+ LivingDamageEvent event = new LivingDamageEvent(entity, src, amount);
+ return (MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount());
+ }
+
public static boolean onLivingDeath(EntityLivingBase entity, DamageSource src)
{
return MinecraftForge.EVENT_BUS.post(new LivingDeathEvent(entity, src));
diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingDamageEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingDamageEvent.java
new file mode 100644
index 0000000000..780f4edad3
--- /dev/null
+++ b/src/main/java/net/minecraftforge/event/entity/living/LivingDamageEvent.java
@@ -0,0 +1,64 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2017.
+ *
+ * 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.entity.living;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.common.ForgeHooks;
+import net.minecraftforge.fml.common.eventhandler.Cancelable;
+import net.minecraft.util.DamageSource;
+import net.minecraft.entity.EntityLivingBase;
+
+/**
+ * LivingDamageEvent is fired just before damage is applied to entity.
+ * At this point armor, potion and absorption modifiers have already been applied to damage - this is FINAL value.
+ * Also note that appropriate resources (like armor durability and absorption extra hearths) have already been consumed.
+ * This event is fired whenever an Entity is damaged in
+ * {@link EntityLivingBase#damageEntity(DamageSource, float)} and
+ * {@link EntityPlayer#damageEntity(DamageSource, float)}.
+ *
+ * This event is fired via the {@link ForgeHooks#onLivingDamage(EntityLivingBase, DamageSource, float)}.
+ *
+ * {@link #source} contains the DamageSource that caused this Entity to be hurt.
+ * {@link #amount} contains the final amount of damage that will be dealt to entity.
+ *
+ * This event is {@link Cancelable}.
+ * If this event is canceled, the Entity is not hurt. Used resources WILL NOT be restored.
+ *
+ * This event does not have a result. {@link HasResult}
+ * @see LivingHurtEvent
+ **/
+@Cancelable
+public class LivingDamageEvent extends LivingEvent
+{
+ private final DamageSource source;
+ private float amount;
+ public LivingDamageEvent(EntityLivingBase entity, DamageSource source, float amount)
+ {
+ super(entity);
+ this.source = source;
+ this.amount = amount;
+ }
+
+ public DamageSource getSource() { return source; }
+
+ public float getAmount() { return amount; }
+
+ public void setAmount(float amount) { this.amount = amount; }
+}
diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingHurtEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingHurtEvent.java
index a3c0a84e0d..11c4a2a4a7 100644
--- a/src/main/java/net/minecraftforge/event/entity/living/LivingHurtEvent.java
+++ b/src/main/java/net/minecraftforge/event/entity/living/LivingHurtEvent.java
@@ -43,6 +43,7 @@ import net.minecraft.entity.EntityLivingBase;
* This event does not have a result. {@link HasResult}
*
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ * @see LivingDamageEvent
**/
@Cancelable
public class LivingHurtEvent extends LivingEvent
diff --git a/src/test/java/net/minecraftforge/debug/LivingDamageEventTest.java b/src/test/java/net/minecraftforge/debug/LivingDamageEventTest.java
new file mode 100644
index 0000000000..8ac95e0d2d
--- /dev/null
+++ b/src/test/java/net/minecraftforge/debug/LivingDamageEventTest.java
@@ -0,0 +1,152 @@
+package net.minecraftforge.debug;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.annotation.Nullable;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.CommandException;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.command.WrongUsageException;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.math.BlockPos;
+import net.minecraftforge.event.entity.living.LivingDamageEvent;
+import net.minecraftforge.event.entity.living.LivingHurtEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
+import net.minecraftforge.fml.common.Mod.EventHandler;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.apache.logging.log4j.Logger;
+
+@Mod(modid = LivingDamageEventTest.MODID, name = "ForgeDebugLivingDamage", version = "1.0", acceptableRemoteVersions = "*")
+public class LivingDamageEventTest
+{
+
+ private static Logger logger;
+
+ public static final String MODID = "forgedebuglivingdamage";
+
+ private static final Map DAMAGE_SOURCES = ImmutableMap. builder()
+ .put("fire", DamageSource.IN_FIRE)
+ .put("lightning_bolt", DamageSource.LIGHTNING_BOLT)
+ .put("on_fire", DamageSource.ON_FIRE)
+ .put("lava", DamageSource.LAVA)
+ .put("hot_floor", DamageSource.HOT_FLOOR)
+ .put("in_wall", DamageSource.IN_WALL)
+ .put("cramming", DamageSource.CRAMMING)
+ .put("drown", DamageSource.DROWN)
+ .put("starve", DamageSource.STARVE)
+ .put("cactus", DamageSource.CACTUS)
+ .put("fall", DamageSource.FALL)
+ .put("fly_into_wall", DamageSource.FLY_INTO_WALL)
+ .put("out_of_world", DamageSource.OUT_OF_WORLD)
+ .put("generic", DamageSource.GENERIC)
+ .put("magic", DamageSource.MAGIC)
+ .put("wither", DamageSource.WITHER)
+ .put("anvil", DamageSource.ANVIL)
+ .put("falling_block", DamageSource.FALLING_BLOCK)
+ .put("dragon_breath", DamageSource.DRAGON_BREATH)
+ .put("fireworks", DamageSource.FIREWORKS)
+ .build();
+
+ private static class CommandDamage extends CommandBase
+ {
+
+ private static final String USAGE = "damage ";
+
+ @Override
+ public String getName()
+ {
+ return "damage";
+ }
+
+ @Override
+ public String getUsage(ICommandSender sender)
+ {
+ return USAGE;
+ }
+
+ @Override
+ public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
+ {
+ if (args.length < 3)
+ {
+ throw new WrongUsageException(USAGE);
+ }
+
+ EntityLivingBase target = getEntity(server, sender, args[0], EntityLivingBase.class);
+ DamageSource damageSource = DAMAGE_SOURCES.get(args[1].toLowerCase(Locale.ROOT));
+ if (target == null || damageSource == null)
+ {
+ throw new WrongUsageException(USAGE);
+ }
+
+ float amount;
+ try
+ {
+ amount = Float.parseFloat(args[2]);
+ } catch (NumberFormatException e)
+ {
+ throw new WrongUsageException(USAGE);
+ }
+
+ target.attackEntityFrom(damageSource, amount);
+ }
+
+ @Override
+ public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
+ {
+ if (args.length == 1)
+ {
+ return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames());
+ } else if (args.length == 2)
+ {
+ return getListOfStringsMatchingLastWord(args, DAMAGE_SOURCES.keySet());
+ }
+
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isUsernameIndex(String[] args, int index)
+ {
+ return index == 0;
+ }
+ }
+
+ @EventHandler
+ public void preInit(FMLPreInitializationEvent event)
+ {
+ logger = event.getModLog();
+ }
+
+ @EventHandler
+ void serverStarting(FMLServerStartingEvent evt)
+ {
+ evt.registerServerCommand(new CommandDamage());
+ }
+
+ @EventBusSubscriber
+ public static class TestEventHandler
+ {
+
+ @SubscribeEvent
+ public static void livingHurtPre(LivingHurtEvent evt)
+ {
+ logger.info("Entity {} damage from {} (pre-reduction): {}", evt.getEntity(), evt.getSource().getDamageType(), evt.getAmount());
+ }
+
+ @SubscribeEvent
+ public static void livingHurtPost(LivingDamageEvent evt)
+ {
+ logger.info("Entity {} damage from {} (post-reduction): {}", evt.getEntity(), evt.getSource().getDamageType(), evt.getAmount());
+ }
+
+ }
+}