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