Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package io.papermc.paper.event.entity;

import org.bukkit.entity.Creeper;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;

/**
* Fired whenever a creeper's swell level changes.
* <p>
* If this event is cancelled, the swell change will not be applied, but the client may still render the swell change.
*/
@NullMarked
public class CreeperSwellEvent extends EntityEvent implements Cancellable {

private static final HandlerList HANDLER_LIST = new HandlerList();

private boolean cancelled;
private int swellChange;
private int finalSwellChange;
private final int currentSwell;
private final int maxSwell;
private final SwellReason swellReason;

@ApiStatus.Internal
public CreeperSwellEvent(final Creeper creeper, int swellChange, int finalSwellChange, int currentSwell,
int maxSwell, SwellReason swellReason) {
super(creeper);
this.swellChange = swellChange;
this.finalSwellChange = finalSwellChange;
this.currentSwell = currentSwell;
this.maxSwell = maxSwell;
this.swellReason = swellReason;
}

/**
* Gets the cancellation state of this event. A cancelled event will not
* be executed in the server, but will still pass to other plugins
*
* @return {@code true} if this event is cancelled
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}

/**
* Sets the cancellation state of this event. A cancelled event will not
* be executed in the server, but will still pass to other plugins.
*
* @param cancel {@code true} if you wish to cancel this event
*/
@Override
public void setCancelled(final boolean cancel) {
this.cancelled = cancel;
}

/**
* Returns the Entity involved in this event
*
* @return Entity who is involved in this event
*/
@Override
public Creeper getEntity() {
return (Creeper) super.getEntity();
}

/**
* Gets the current swell level of the {@link Creeper}
*
* @return current swell level of the {@link Creeper}
*/
public int getCurrentSwell() {
return this.currentSwell;
}

/**
* Gets the current maximum swell level of the {@link Creeper}
*
* @return maximum swell level of the {@link Creeper}
*/
public int getMaxSwell() {
return this.maxSwell;
}

/**
* Gets the raw swell change before clamping
*
* @return the raw swell change
*/
public int getSwellChange() {
return this.swellChange;
}

/**
* Gets the final swell change that will actually be applied to the {@link Creeper}
*
* @return the final swell change
*/
public int getFinalSwellChange() {
return this.finalSwellChange;
}

/**
* Sets the swell change to apply to the {@link Creeper}
* <p>
* The swell change will be clamped to keep the {@link Creeper}'s swell level within [0, maxSwell]
*
* @param newSwellChange the swell change to apply
* @see #getFinalSwellChange()
*/
public void setSwellChange(int newSwellChange) {
this.swellChange = newSwellChange;
// don't allow creeper swell level to go below 0 or above maxSwell
if (this.currentSwell + newSwellChange < 0) {
this.finalSwellChange = -this.currentSwell;
} else if (this.currentSwell + newSwellChange > this.maxSwell) {
this.finalSwellChange = this.maxSwell - this.currentSwell;
} else {
this.finalSwellChange = newSwellChange;
}
}

/**
* Gets the reason for the swell change
*
* @return a SwellReason value detailing the cause of the swell change
*/
public SwellReason getSwellReason() {
return this.swellReason;
}

@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}

public static HandlerList getHandlerList() {
return HANDLER_LIST;
}

/**
* An enum to specify the reason for a creeper swell change
*/
public enum SwellReason {
/**
* Swelling caused by the creeper being primed (by proximity or ignition)
*/
PRIMED,
/**
* Swelling caused by the creeper falling from a height
*/
FALL_DAMAGE,
/**
* Swelling caused by custom behavior, such as a plugin
*/
CUSTOM
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@

public Creeper(final EntityType<? extends Creeper> type, final Level level) {
super(type, level);
@@ -86,10 +_,20 @@
@Override
public boolean causeFallDamage(final double fallDistance, final float damageModifier, final DamageSource damageSource) {
boolean damaged = super.causeFallDamage(fallDistance, damageModifier, damageSource);
- this.swell += (int)(fallDistance * 1.5);
- if (this.swell > this.maxSwell - 5) {
- this.swell = this.maxSwell - 5;
+ // Paper start - CreeperSwellEvent; fire event for fall damage swell
+ int swellChange = (int)(fallDistance * 1.5);
+ int maxAllowedSwell = this.maxSwell - 5;
+ int finalSwellChange = Math.min(swellChange, maxAllowedSwell - this.swell);
+ if (finalSwellChange > 0) {
+ io.papermc.paper.event.entity.CreeperSwellEvent event = new io.papermc.paper.event.entity.CreeperSwellEvent(
+ (org.bukkit.entity.Creeper) this.getBukkitEntity(), swellChange, finalSwellChange, this.swell,
+ this.maxSwell, io.papermc.paper.event.entity.CreeperSwellEvent.SwellReason.FALL_DAMAGE);
+ event.callEvent();
+ if (!event.isCancelled()) {
+ this.swell += event.getFinalSwellChange();
+ }
}
+ // Paper end - CreeperSwellEvent

return damaged;
}
@@ -118,7 +_,7 @@
this.maxSwell = input.getShortOr("Fuse", (short)30);
this.explosionRadius = input.getByteOr("ExplosionRadius", (byte)3);
Expand All @@ -17,6 +41,35 @@
}
}

@@ -131,12 +_,23 @@
}

int swellDir = this.getSwellDir();
- if (swellDir > 0 && this.swell == 0) {
- this.playSound(SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F);
- this.gameEvent(GameEvent.PRIME_FUSE);
+ // Paper start - CreeperSwellEvent; fire if swell will change this tick
+ if (swellDir != 0) {
+ int finalSwellChange = Math.clamp(swellDir, -this.swell, this.maxSwell - this.swell);
+ io.papermc.paper.event.entity.CreeperSwellEvent event = new io.papermc.paper.event.entity.CreeperSwellEvent(
+ (org.bukkit.entity.Creeper) this.getBukkitEntity(), swellDir, finalSwellChange, this.swell, this.maxSwell,
+ io.papermc.paper.event.entity.CreeperSwellEvent.SwellReason.PRIMED);
+ event.callEvent();
+ if (!event.isCancelled()) {
+ int change = event.getFinalSwellChange();
+ if (change > 0 && this.swell == 0) {
+ this.playSound(SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F);
+ this.gameEvent(GameEvent.PRIME_FUSE);
+ }
+ this.swell += change;
+ }
}
-
- this.swell += swellDir;
+ // Paper end - CreeperSwellEvent
if (this.swell < 0) {
this.swell = 0;
}
@@ -151,10 +_,11 @@
}

Expand Down Expand Up @@ -63,7 +116,7 @@
itemStack.shrink(1);
} else {
itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot());
@@ -231,18 +_,29 @@
@@ -231,18 +_,38 @@
public void explodeCreeper() {
if (this.level() instanceof ServerLevel level) {
float explosionMultiplier = this.isPowered() ? 2.0F : 1.0F;
Expand All @@ -80,7 +133,16 @@
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
+ // CraftBukkit start
+ } else {
+ this.swell = 0;
+ // Paper start - CreeperSwellEvent; reset swell through event when explosion is cancelled
+ io.papermc.paper.event.entity.CreeperSwellEvent swellEvent =
+ new io.papermc.paper.event.entity.CreeperSwellEvent((org.bukkit.entity.Creeper) getBukkitEntity(),
+ -this.swell, -this.swell, this.swell, this.maxSwell,
+ io.papermc.paper.event.entity.CreeperSwellEvent.SwellReason.CUSTOM);
+ swellEvent.callEvent();
+ if (!swellEvent.isCancelled()) {
+ this.swell += swellEvent.getFinalSwellChange();
+ }
+ // Paper end - CreeperSwellEvent
+ this.entityData.set(DATA_IS_IGNITED, false); // Paper
+ }
+ // CraftBukkit end
Expand Down
Loading