diff --git a/patches/minecraft/net/minecraft/entity/merchant/villager/VillagerEntity.java.patch b/patches/minecraft/net/minecraft/entity/merchant/villager/VillagerEntity.java.patch index e8f89fa4e..2ff4f178a 100644 --- a/patches/minecraft/net/minecraft/entity/merchant/villager/VillagerEntity.java.patch +++ b/patches/minecraft/net/minecraft/entity/merchant/villager/VillagerEntity.java.patch @@ -19,3 +19,20 @@ } @OnlyIn(Dist.CLIENT) +@@ -687,7 +688,7 @@ + } + + public void func_241841_a(ServerWorld p_241841_1_, LightningBoltEntity p_241841_2_) { +- if (p_241841_1_.func_175659_aa() != Difficulty.PEACEFUL) { ++ if (p_241841_1_.func_175659_aa() != Difficulty.PEACEFUL && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_200759_ay, (timer) -> {})) { + field_184243_a.info("Villager {} was struck by lightning {}.", this, p_241841_2_); + WitchEntity witchentity = EntityType.field_200759_ay.func_200721_a(p_241841_1_); + witchentity.func_70012_b(this.func_226277_ct_(), this.func_226278_cu_(), this.func_226281_cx_(), this.field_70177_z, this.field_70125_A); +@@ -699,6 +700,7 @@ + } + + witchentity.func_110163_bv(); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, witchentity); + p_241841_1_.func_242417_l(witchentity); + this.func_242369_fq(); + this.func_70106_y(); diff --git a/patches/minecraft/net/minecraft/entity/monster/HoglinEntity.java.patch b/patches/minecraft/net/minecraft/entity/monster/HoglinEntity.java.patch new file mode 100644 index 000000000..fffae2a9c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/HoglinEntity.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/entity/monster/HoglinEntity.java ++++ b/net/minecraft/entity/monster/HoglinEntity.java +@@ -117,7 +117,7 @@ + HoglinTasks.func_234377_a_(this); + if (this.func_234364_eK_()) { + ++this.field_234358_by_; +- if (this.field_234358_by_ > 300) { ++ if (this.field_234358_by_ > 300 && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_233590_aW_, (timer) -> this.field_234358_by_ = timer)) { + this.func_241412_a_(SoundEvents.field_232715_fE_); + this.func_234360_a_((ServerWorld)this.field_70170_p); + } +@@ -212,6 +212,7 @@ + ZoglinEntity zoglinentity = this.func_233656_b_(EntityType.field_233590_aW_, true); + if (zoglinentity != null) { + zoglinentity.func_195064_c(new EffectInstance(Effects.field_76431_k, 200, 0)); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, zoglinentity); + } + + } diff --git a/patches/minecraft/net/minecraft/entity/monster/ZombieEntity.java.patch b/patches/minecraft/net/minecraft/entity/monster/ZombieEntity.java.patch index 9b4b29a3b..b97390103 100644 --- a/patches/minecraft/net/minecraft/entity/monster/ZombieEntity.java.patch +++ b/patches/minecraft/net/minecraft/entity/monster/ZombieEntity.java.patch @@ -1,6 +1,24 @@ --- a/net/minecraft/entity/monster/ZombieEntity.java +++ b/net/minecraft/entity/monster/ZombieEntity.java -@@ -268,12 +268,16 @@ +@@ -186,7 +186,8 @@ + if (!this.field_70170_p.field_72995_K && this.func_70089_S() && !this.func_175446_cd()) { + if (this.func_204706_dD()) { + --this.field_204708_bE; +- if (this.field_204708_bE < 0) { ++ ++ if (this.field_204708_bE < 0 && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_200725_aD, (timer) -> this.field_204708_bE = timer)) { + this.func_207302_dI(); + } + } else if (this.func_204703_dA()) { +@@ -248,6 +249,7 @@ + if (zombieentity != null) { + zombieentity.func_207304_a(zombieentity.field_70170_p.func_175649_E(zombieentity.func_233580_cy_()).func_180170_c()); + zombieentity.func_146070_a(zombieentity.func_204900_dz() && this.func_146072_bX()); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, zombieentity); + } + + } +@@ -268,12 +270,16 @@ livingentity = (LivingEntity)p_70097_1_.func_76346_g(); } @@ -19,7 +37,7 @@ for(int l = 0; l < 50; ++l) { int i1 = i + MathHelper.func_76136_a(this.field_70146_Z, 7, 40) * MathHelper.func_76136_a(this.field_70146_Z, -1, 1); int j1 = j + MathHelper.func_76136_a(this.field_70146_Z, 7, 40) * MathHelper.func_76136_a(this.field_70146_Z, -1, 1); -@@ -284,6 +288,7 @@ +@@ -284,6 +290,7 @@ if (WorldEntitySpawner.func_209382_a(entityspawnplacementregistry$placementtype, this.field_70170_p, blockpos, entitytype) && EntitySpawnPlacementRegistry.func_223515_a(entitytype, serverworld, SpawnReason.REINFORCEMENT, blockpos, this.field_70170_p.field_73012_v)) { zombieentity.func_70107_b((double)i1, (double)j1, (double)k1); if (!this.field_70170_p.func_217358_a((double)i1, (double)j1, (double)k1, 7.0D) && this.field_70170_p.func_226668_i_(zombieentity) && this.field_70170_p.func_226669_j_(zombieentity) && !this.field_70170_p.func_72953_d(zombieentity.func_174813_aQ())) { @@ -27,7 +45,24 @@ zombieentity.func_70624_b(livingentity); zombieentity.func_213386_a(serverworld, this.field_70170_p.func_175649_E(zombieentity.func_233580_cy_()), SpawnReason.REINFORCEMENT, (ILivingEntityData)null, (CompoundNBT)null); serverworld.func_242417_l(zombieentity); -@@ -448,7 +453,7 @@ +@@ -369,7 +376,7 @@ + + public void func_241847_a(ServerWorld p_241847_1_, LivingEntity p_241847_2_) { + super.func_241847_a(p_241847_1_, p_241847_2_); +- if ((p_241847_1_.func_175659_aa() == Difficulty.NORMAL || p_241847_1_.func_175659_aa() == Difficulty.HARD) && p_241847_2_ instanceof VillagerEntity) { ++ if ((p_241847_1_.func_175659_aa() == Difficulty.NORMAL || p_241847_1_.func_175659_aa() == Difficulty.HARD) && p_241847_2_ instanceof VillagerEntity && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(p_241847_2_, EntityType.field_200727_aF, (timer) -> {})) { + if (p_241847_1_.func_175659_aa() != Difficulty.HARD && this.field_70146_Z.nextBoolean()) { + return; + } +@@ -381,6 +388,7 @@ + zombievillagerentity.func_223727_a(villagerentity.func_223722_es().func_234058_a_(NBTDynamicOps.field_210820_a).getValue()); + zombievillagerentity.func_213790_g(villagerentity.func_213706_dY().func_222199_a()); + zombievillagerentity.func_213789_a(villagerentity.func_213708_dV()); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(p_241847_2_, zombievillagerentity); + if (!this.func_174814_R()) { + p_241847_1_.func_217378_a((PlayerEntity)null, 1026, this.func_233580_cy_(), 0); + } +@@ -448,7 +456,7 @@ } public static boolean func_241399_a_(Random p_241399_0_) { @@ -36,7 +71,7 @@ } protected void func_207304_a(float p_207304_1_) { -@@ -468,7 +473,7 @@ +@@ -468,7 +476,7 @@ } protected void func_230291_eT_() { diff --git a/patches/minecraft/net/minecraft/entity/monster/ZombieVillagerEntity.java.patch b/patches/minecraft/net/minecraft/entity/monster/ZombieVillagerEntity.java.patch new file mode 100644 index 000000000..dd22654a7 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/ZombieVillagerEntity.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/entity/monster/ZombieVillagerEntity.java ++++ b/net/minecraft/entity/monster/ZombieVillagerEntity.java +@@ -116,7 +116,7 @@ + if (!this.field_70170_p.field_72995_K && this.func_70089_S() && this.func_82230_o()) { + int i = this.func_190735_dq(); + this.field_82234_d -= i; +- if (this.field_82234_d <= 0) { ++ if (this.field_82234_d <= 0 && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_200756_av, (timer) -> this.field_82234_d = timer)) { + this.func_213791_a((ServerWorld)this.field_70170_p); + } + } +@@ -218,7 +218,7 @@ + if (!this.func_174814_R()) { + p_213791_1_.func_217378_a((PlayerEntity)null, 1027, this.func_233580_cy_(), 0); + } +- ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, villagerentity); + } + + private int func_190735_dq() { diff --git a/patches/minecraft/net/minecraft/entity/monster/piglin/AbstractPiglinEntity.java.patch b/patches/minecraft/net/minecraft/entity/monster/piglin/AbstractPiglinEntity.java.patch new file mode 100644 index 000000000..341d14fed --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/piglin/AbstractPiglinEntity.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/entity/monster/piglin/AbstractPiglinEntity.java ++++ b/net/minecraft/entity/monster/piglin/AbstractPiglinEntity.java +@@ -83,7 +83,7 @@ + this.field_242334_c = 0; + } + +- if (this.field_242334_c > 300) { ++ if (this.field_242334_c > 300 && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_233592_ba_, (timer) -> this.field_242334_c = timer)) { + this.func_241848_eP(); + this.func_234416_a_((ServerWorld)this.field_70170_p); + } +@@ -98,6 +98,7 @@ + ZombifiedPiglinEntity zombifiedpiglinentity = this.func_233656_b_(EntityType.field_233592_ba_, true); + if (zombifiedpiglinentity != null) { + zombifiedpiglinentity.func_195064_c(new EffectInstance(Effects.field_76431_k, 200, 0)); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, zombifiedpiglinentity); + } + + } diff --git a/patches/minecraft/net/minecraft/entity/passive/PigEntity.java.patch b/patches/minecraft/net/minecraft/entity/passive/PigEntity.java.patch new file mode 100644 index 000000000..4b1c7b184 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/PigEntity.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/entity/passive/PigEntity.java ++++ b/net/minecraft/entity/passive/PigEntity.java +@@ -203,7 +203,7 @@ + } + + public void func_241841_a(ServerWorld p_241841_1_, LightningBoltEntity p_241841_2_) { +- if (p_241841_1_.func_175659_aa() != Difficulty.PEACEFUL) { ++ if (p_241841_1_.func_175659_aa() != Difficulty.PEACEFUL && net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityType.field_233592_ba_, (timer) -> {})) { + ZombifiedPiglinEntity zombifiedpiglinentity = EntityType.field_233592_ba_.func_200721_a(p_241841_1_); + zombifiedpiglinentity.func_184201_a(EquipmentSlotType.MAINHAND, new ItemStack(Items.field_151010_B)); + zombifiedpiglinentity.func_70012_b(this.func_226277_ct_(), this.func_226278_cu_(), this.func_226281_cx_(), this.field_70177_z, this.field_70125_A); +@@ -215,6 +215,7 @@ + } + + zombifiedpiglinentity.func_110163_bv(); ++ net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, zombifiedpiglinentity); + p_241841_1_.func_217376_c(zombifiedpiglinentity); + this.func_70106_y(); + } else { diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index 60ff7fae5..40f435b17 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -21,6 +21,7 @@ package net.minecraftforge.event; import java.io.File; import java.util.*; +import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -36,6 +37,7 @@ import net.minecraft.command.Commands; import net.minecraft.entity.Entity; import net.minecraft.entity.Pose; import net.minecraft.entity.EntitySize; +import net.minecraft.entity.EntityType; import net.minecraft.entity.MobEntity; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.LivingEntity; @@ -103,6 +105,7 @@ import net.minecraftforge.event.entity.PlaySoundAtEntityEvent; import net.minecraftforge.event.entity.ProjectileImpactEvent; import net.minecraftforge.event.entity.item.ItemExpireEvent; import net.minecraftforge.event.entity.living.AnimalTameEvent; +import net.minecraftforge.event.entity.living.LivingConversionEvent; import net.minecraftforge.event.entity.living.LivingDestroyBlockEvent; import net.minecraftforge.event.entity.living.LivingEntityUseItemEvent; import net.minecraftforge.event.entity.living.LivingExperienceDropEvent; @@ -748,4 +751,14 @@ public class ForgeEventFactory MinecraftForge.EVENT_BUS.post(evt); return evt; } + + public static boolean canLivingConvert(LivingEntity entity, EntityType outcome, Consumer timer) + { + return !MinecraftForge.EVENT_BUS.post(new LivingConversionEvent.Pre(entity, outcome, timer)); + } + + public static void onLivingConvert(LivingEntity entity, LivingEntity outcome) + { + MinecraftForge.EVENT_BUS.post(new LivingConversionEvent.Post(entity, outcome)); + } } diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingConversionEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingConversionEvent.java new file mode 100644 index 000000000..951c79231 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/entity/living/LivingConversionEvent.java @@ -0,0 +1,108 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * 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.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraftforge.eventbus.api.Cancelable; + +import java.util.function.Consumer; + +public class LivingConversionEvent extends LivingEvent +{ + public LivingConversionEvent(LivingEntity entity) + { + super(entity); + } + + /** + * LivingConversionEvent.Pre is triggered when an entity is trying + * to replace itself with another entity + * + * This event may trigger every tick even if it was cancelled last tick + * for entities like Zombies and Hoglins. To prevent it, the conversion + * timer needs to be changed or reset + * + * This event is {@link Cancelable} + * If cancelled, the replacement will not occur + */ + @Cancelable + public static class Pre extends LivingConversionEvent + { + private final EntityType outcome; + private final Consumer timer; + + public Pre(LivingEntity entity, EntityType outcome, Consumer timer) + { + super(entity); + this.outcome = outcome; + this.timer = timer; + } + + /** + * Gets the entity type of the new entity this living entity is + * converting to + * @return the entity type of the new entity + */ + public EntityType getOutcome() + { + return outcome; + } + + /** + * Sets the conversion timer, by changing this it prevents the + * event being triggered every tick + * Do note the timer of some of the entities are increments, but + * some of them are decrements + * Not every conversion is applicable for this + * @param ticks timer ticks + */ + public void setConversionTimer(int ticks) + { + timer.accept(ticks); + } + } + + /** + * LivingConversionEvent.Post is triggered when an entity is replacing + * itself with another entity. + * The old living entity is likely to be removed right after this event. + */ + public static class Post extends LivingConversionEvent + { + private final LivingEntity outcome; + + public Post(LivingEntity entity, LivingEntity outcome) + { + super(entity); + this.outcome = outcome; + } + + /** + * Gets the finalized new entity (with all data like potion + * effect and equipments set) + * @return the finalized new entity + */ + public LivingEntity getOutcome() + { + return outcome; + } + } +} diff --git a/src/test/java/net/minecraftforge/debug/entity/living/LivingConversionEventTest.java b/src/test/java/net/minecraftforge/debug/entity/living/LivingConversionEventTest.java new file mode 100644 index 000000000..75fc5adf7 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/entity/living/LivingConversionEventTest.java @@ -0,0 +1,53 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * 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.entity.living; + +import net.minecraft.entity.merchant.villager.VillagerEntity; +import net.minecraft.entity.monster.piglin.PiglinEntity; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingConversionEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod("living_conversion_event_test") +public class LivingConversionEventTest +{ + public LivingConversionEventTest() + { + MinecraftForge.EVENT_BUS.addListener(this::canLivingConversion); + MinecraftForge.EVENT_BUS.addListener(this::onLivingConversion); + } + + public void canLivingConversion(LivingConversionEvent.Pre event) + { + if (event.getEntityLiving() instanceof PiglinEntity) + { + event.setCanceled(true); + event.setConversionTimer(0); + } + } + + public void onLivingConversion(LivingConversionEvent.Post event) + { + if (event.getEntityLiving() instanceof VillagerEntity) + event.getEntityLiving().addPotionEffect(new EffectInstance(Effects.LUCK, 20)); + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 3eafb29be..5c23c0cfb 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -88,6 +88,8 @@ license="LGPL v2.1" modId="custom_tag_types_test" [[mods]] modId="biome_loading_event_test" +[[mods]] + modId="living_conversion_event_test" [[mods]] modId="player_game_mode_event_test" [[mods]]