diff --git a/fmlearlydisplay/src/main/resources/forgejwst.png b/fmlearlydisplay/src/main/resources/forgejwst.png deleted file mode 100644 index 010942e591..0000000000 Binary files a/fmlearlydisplay/src/main/resources/forgejwst.png and /dev/null differ diff --git a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch index 419b29eaab..71ec27eb89 100644 --- a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch @@ -53,11 +53,12 @@ public boolean m_6040_() { return this.m_6336_() == MobType.f_21641_; } -@@ -352,6 +_,8 @@ +@@ -352,6 +_,9 @@ } } -+ net.minecraftforge.common.ForgeHooks.onLivingBreathe(this, -m_7302_(0), m_7305_(0)); ++ int airSupply = this.m_20146_(); ++ net.minecraftforge.common.ForgeHooks.onLivingBreathe(this, airSupply - m_7302_(airSupply), m_7305_(airSupply) - airSupply); + if (false) // Forge: Handled in ForgeHooks#onLivingBreathe(LivingEntity, int, int) if (this.m_204029_(FluidTags.f_13131_) && !this.m_9236_().m_8055_(BlockPos.m_274561_(this.m_20185_(), this.m_20188_(), this.m_20189_())).m_60713_(Blocks.f_50628_)) { boolean flag1 = !this.m_6040_() && !MobEffectUtil.m_19588_(this) && (!flag || !((Player)this).m_150110_().f_35934_); diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 34dc8a9681..b1efb192d8 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -1565,16 +1565,22 @@ public class ForgeHooks * @param entity The living entity which is currently updated * @param consumeAirAmount The amount of air to consume when the entity is unable to breathe * @param refillAirAmount The amount of air to refill when the entity is able to breathe + * @implNote This method needs to closely replicate the logic found right after the call site in {@link LivingEntity#baseTick()} as it overrides it. */ public static void onLivingBreathe(LivingEntity entity, int consumeAirAmount, int refillAirAmount) { + // Check things that vanilla considers to be air - these will cause the air supply to be increased. boolean isAir = entity.getEyeInFluidType().isAir() || entity.level().getBlockState(BlockPos.containing(entity.getX(), entity.getEyeY(), entity.getZ())).is(Blocks.BUBBLE_COLUMN); - boolean canBreathe = isAir || !entity.canDrownInFluidType(entity.getEyeInFluidType()) || MobEffectUtil.hasWaterBreathing(entity) || (entity instanceof Player && ((Player) entity).getAbilities().invulnerable); - LivingBreatheEvent breatheEvent = new LivingBreatheEvent(entity, canBreathe, consumeAirAmount, refillAirAmount); + // The following effects cause the entity to not drown, but do not cause the air supply to be increased. + boolean canBreathe = !entity.canDrownInFluidType(entity.getEyeInFluidType()) || MobEffectUtil.hasWaterBreathing(entity) || (entity instanceof Player && ((Player) entity).getAbilities().invulnerable); + LivingBreatheEvent breatheEvent = new LivingBreatheEvent(entity, isAir || canBreathe, consumeAirAmount, refillAirAmount, isAir); MinecraftForge.EVENT_BUS.post(breatheEvent); if (breatheEvent.canBreathe()) { - entity.setAirSupply(Math.min(entity.getAirSupply() + breatheEvent.getRefillAirAmount(), entity.getMaxAirSupply())); + if (breatheEvent.canRefillAir()) + { + entity.setAirSupply(Math.min(entity.getAirSupply() + breatheEvent.getRefillAirAmount(), entity.getMaxAirSupply())); + } } else { @@ -1583,13 +1589,13 @@ public class ForgeHooks if (entity.getAirSupply() <= 0) { - LivingDrownEvent drownEvent = new LivingDrownEvent(entity, entity.getAirSupply() <= -20); + LivingDrownEvent drownEvent = new LivingDrownEvent(entity, entity.getAirSupply() <= -20, 2.0F, 8); if (!MinecraftForge.EVENT_BUS.post(drownEvent) && drownEvent.isDrowning()) { entity.setAirSupply(0); Vec3 vec3 = entity.getDeltaMovement(); - for (int i = 0; i < 8; ++i) + for (int i = 0; i < drownEvent.getBubbleCount(); ++i) { double d2 = entity.getRandom().nextDouble() - entity.getRandom().nextDouble(); double d3 = entity.getRandom().nextDouble() - entity.getRandom().nextDouble(); @@ -1597,11 +1603,11 @@ public class ForgeHooks entity.level().addParticle(ParticleTypes.BUBBLE, entity.getX() + d2, entity.getY() + d3, entity.getZ() + d4, vec3.x, vec3.y, vec3.z); } - entity.hurt(entity.damageSources().drown(), 2.0F); + if (drownEvent.getDamageAmount() > 0) entity.hurt(entity.damageSources().drown(), drownEvent.getDamageAmount()); } } - if (isAir && !entity.level().isClientSide && entity.isPassenger() && entity.getVehicle() != null && !entity.getVehicle().canBeRiddenUnderFluidType(entity.getEyeInFluidType(), entity)) + if (!isAir && !entity.level().isClientSide && entity.isPassenger() && entity.getVehicle() != null && !entity.getVehicle().canBeRiddenUnderFluidType(entity.getEyeInFluidType(), entity)) { entity.stopRiding(); } diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingBreatheEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingBreatheEvent.java index 1aa57ce33c..db9763dacf 100644 --- a/src/main/java/net/minecraftforge/event/entity/living/LivingBreatheEvent.java +++ b/src/main/java/net/minecraftforge/event/entity/living/LivingBreatheEvent.java @@ -5,64 +5,109 @@ package net.minecraftforge.event.entity.living; +import org.jetbrains.annotations.ApiStatus; + import net.minecraft.world.entity.LivingEntity; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.Cancelable; /** * LivingBreatheEvent is fired whenever a living entity ticks.
*
- * This event is fired via the {@link ForgeHooks#onLivingBreathe(LivingEntity, int, int)}.
- *
- * {@link #canBreathe()} contains if this entity can breathe.
- * {@link #getConsumeAirAmount()} contains the amount of air that will be consumed if this entity can't breathe.
- * {@link #getRefillAirAmount()} contains the amount of air that will be refilled if this entity can breathe.
+ * This event is fired via {@link ForgeHooks#onLivingBreathe(LivingEntity, int, int)}.
*
* This event is not {@link Cancelable}.
*
* This event does not have a result. {@link HasResult} - **/ -public class LivingBreatheEvent extends LivingEvent -{ + *
+ * This event is fired on {@link MinecraftForge#EVENT_BUS} + */ +public class LivingBreatheEvent extends LivingEvent { private boolean canBreathe; + private boolean canRefillAir; private int consumeAirAmount; private int refillAirAmount; - public LivingBreatheEvent(LivingEntity entity, boolean canBreathe, int consumeAirAmount, int refillAirAmount) - { + @ApiStatus.Internal + @Deprecated(forRemoval = true, since = "1.20.1") + public LivingBreatheEvent(LivingEntity entity, boolean canBreathe, int consumeAirAmount, int refillAirAmount) { + this(entity, canBreathe, consumeAirAmount, refillAirAmount, canBreathe); + } + + @ApiStatus.Internal + public LivingBreatheEvent(LivingEntity entity, boolean canBreathe, int consumeAirAmount, int refillAirAmount, boolean canRefillAir) { super(entity); this.canBreathe = canBreathe; + this.canRefillAir = canRefillAir; this.consumeAirAmount = Math.max(consumeAirAmount, 0); this.refillAirAmount = Math.max(refillAirAmount, 0); } - public boolean canBreathe() - { + /** + * If the entity can breathe and {@link #canRefillAir()} returns true, their air value will be increased by {@link #getRefillAirAmount()}.
+ * If the entity can breathe and {@link #canRefillAir()} returns false, their air value will stay the same.
+ * If the entity cannot breathe, their air value will be reduced by {@link #getConsumeAirAmount()}. + * @return True if the entity can breathe + */ + public boolean canBreathe() { return canBreathe; } - public void setCanBreathe(boolean canBreathe) - { + /** + * Sets if the entity can breathe or not. + * @param canBreathe The new value. + */ + public void setCanBreathe(boolean canBreathe) { this.canBreathe = canBreathe; } - public int getConsumeAirAmount() - { + /** + * If the entity can breathe, {@link #canRefillAir()} will be checked to see if their air value should be refilled. + * @return True if the entity can refill its air value + */ + public boolean canRefillAir() { + return canRefillAir; + } + + /** + * Sets if the entity can refill its air value or not. + * @param canRefillAir The new value. + */ + public void setCanRefillAir(boolean canRefillAir) { + this.canRefillAir = canRefillAir; + } + + /** + * @return The amount the entity's {@linkplain LivingEntity#getAirSupply() air supply} will be reduced by if the entity {@linkplain #canBreathe() cannot breathe}. + */ + public int getConsumeAirAmount() { return consumeAirAmount; } - public void setConsumeAirAmount(int consumeAirAmount) - { + /** + * Sets the new consumed air amount. + * @param consumeAirAmount The new value. + * @see #getConsumeAirAmount() + */ + public void setConsumeAirAmount(int consumeAirAmount) { this.consumeAirAmount = Math.max(consumeAirAmount, 0); } - public int getRefillAirAmount() - { + /** + * @return The amount the entity's {@linkplain LivingEntity#getAirSupply() air supply} will be increased by if the entity {@linkplain #canBreathe() can breathe}. + */ + public int getRefillAirAmount() { return refillAirAmount; } - public void setRefillAirAmount(int refillAirAmount) - { + /** + * Sets the new refilled air amount. + * + * @param refillAirAmount The new value. + * @see #getRefillAirAmount() + */ + public void setRefillAirAmount(int refillAirAmount) { this.refillAirAmount = Math.max(refillAirAmount, 0); } } \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingDrownEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingDrownEvent.java index a327f3f4a0..dfcd5f75fd 100644 --- a/src/main/java/net/minecraftforge/event/entity/living/LivingDrownEvent.java +++ b/src/main/java/net/minecraftforge/event/entity/living/LivingDrownEvent.java @@ -5,42 +5,127 @@ package net.minecraftforge.event.entity.living; +import org.jetbrains.annotations.ApiStatus; + +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.world.damagesource.DamageSources; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.Cancelable; /** - * LivingDrownEvent is fired whenever a living entity can't breathe and its air aupply is less than or equal to - * zero.
- *
- * This event is fired via the {@link ForgeHooks#onLivingBreathe(LivingEntity, int, int)}.
- *
- * {@link #isDrowning()} contains if this entity is drowning.
- *
- * This event is {@link Cancelable}.
- * If this event is canceled, this entity won't take drowning damage, no particles will be spawned and the air supply - * won't be reset to zero ignoring if {@link #isDrowning()} is true or not.
- *
- * This event does not have a result. {@link HasResult} + * LivingDrownEvent is fired whenever a living entity can't breathe and its air supply is less than or equal to zero. + *

+ * This event is fired via {@link ForgeHooks#onLivingBreathe(LivingEntity, int, int)}. + *

+ * This event is {@link Cancelable}. Effects of cancellation are noted in {@link #setCanceled(boolean)}. + *

+ * This event does not {@linkplain HasResult have a result}. + * This event is fired on {@link MinecraftForge#EVENT_BUS} **/ @Cancelable -public class LivingDrownEvent extends LivingEvent -{ +public class LivingDrownEvent extends LivingEvent { private boolean isDrowning; + private float damageAmount; + private int bubbleCount; - public LivingDrownEvent(LivingEntity entity, boolean isDrowning) - { + /** + * Constructs a new LivingDrownEvent. + * + * @param entity The entity that is drowning. + * @param isDrowning If the entity is "actively" drowning, and would take damage. + * @param damageAmount The amount of {@linkplain DamageSources#drown() drowning damage} the entity would take. + * @param bubbleCount The number of {@linkplain ParticleTypes#BUBBLE} particles that will be spawned when actively drowning. + */ + @ApiStatus.Internal + public LivingDrownEvent(LivingEntity entity, boolean isDrowning, float damageAmount, int bubbleCount) { super(entity); this.isDrowning = isDrowning; + this.damageAmount = damageAmount; + this.bubbleCount = bubbleCount; } - public boolean isDrowning() - { + + @ApiStatus.Internal + @Deprecated(forRemoval = true, since = "1.20.1") + public LivingDrownEvent(LivingEntity entity, boolean isDrowning) { + this(entity, isDrowning, 2.0F, 8); + } + + /** + * This method returns true if the entity is "actively" drowning.
+ * For most entities, this happens when their air supply reaches -20.
+ * When this is true, the entity will take damage, spawn particles, and reset their air supply to 0. + * + * @return If the entity is actively drowning. + */ + public boolean isDrowning() { return isDrowning; } - public void setDrowning(boolean isDrowning) - { + /** + * Sets if the entity is actively drowning. + * + * @param isDrowning The new value. + * @see #isDrowning() + */ + public void setDrowning(boolean isDrowning) { this.isDrowning = isDrowning; } + + /** + * Gets the amount of {@linkplain DamageSources#drown() drowning damage} the entity would take.
+ * Drowning damage is only inflicted if the entity is {@linkplain #isDrowning() actively drowning}.
+ * For vanilla entities, the default amount of damage is 2 (1 heart). + *

+ * If the damage amount is less than or equal to zero, {@link Entity#hurt} will not be called. + * + * @return The amount of damage that will be dealt to the entity when actively drowning. + */ + public float getDamageAmount() { + return damageAmount; + } + + /** + * Sets the amount of drowning damage that may be inflicted. + * + * @param damageAmount The new value. + * @see #getDamageAmount() + */ + public void setDamageAmount(float damageAmount) { + this.damageAmount = damageAmount; + } + + /** + * Gets the number of {@linkplain ParticleTypes#BUBBLE} particles that would be spawned.
+ * Bubbles are only spawned if the entity is {@linkplain #isDrowning() actively drowning}.
+ * For vanilla entities, the default value is 8 particles. + * + * @return The number of bubble particles that will spawn when actively drowning. + */ + public int getBubbleCount() { + return bubbleCount; + } + + /** + * Sets the amount of bubbles that may be spawned. + * + * @param bubbleCount The new value. + * @see #getBubbleCount() + */ + public void setBubbleCount(int bubbleCount) { + this.bubbleCount = bubbleCount; + } + + /** + * Cancels the drowning event.
+ * Cancellation is mostly equivalent to {@link #setDrowning(boolean)} with a value of false.
+ * However, this also incurs the usual side effects of cancellation. + */ + @Override + public void setCanceled(boolean cancel) { + super.setCanceled(cancel); + } } \ No newline at end of file