diff --git a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java index 20b5120a0f1..561a02567cb 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java @@ -20,6 +20,11 @@ import ch.njol.skript.Skript; import ch.njol.util.Math2; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; @@ -28,6 +33,7 @@ import org.bukkit.entity.Damageable; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Constructor; @@ -46,7 +52,7 @@ public static double getHealth(Damageable e) { return 0; return e.getHealth() / 2; } - + /** * Set the health of an entity * @param e Entity to set health for @@ -55,7 +61,7 @@ public static double getHealth(Damageable e) { public static void setHealth(Damageable e, double health) { e.setHealth(Math2.fit(0, health, getMaxHealth(e)) * 2); } - + /** * Get the max health an entity has * @param e Entity to get max health from @@ -66,7 +72,7 @@ public static double getMaxHealth(Damageable e) { assert attributeInstance != null; return attributeInstance.getValue() / 2; } - + /** * Set the max health an entity can have * @param e Entity to set max health for @@ -77,7 +83,7 @@ public static void setMaxHealth(Damageable e, double health) { assert attributeInstance != null; attributeInstance.setBaseValue(health * 2); } - + /** * Apply damage to an entity * @param e Entity to apply damage to @@ -103,39 +109,58 @@ public static void heal(Damageable e, double h) { } setHealth(e, getHealth(e) + h); } - + public static double getDamage(EntityDamageEvent e) { return e.getDamage() / 2; } - + public static double getFinalDamage(EntityDamageEvent e) { return e.getFinalDamage() / 2; } - + public static void setDamage(EntityDamageEvent e, double damage) { e.setDamage(damage * 2); } + /** + * In a late 1.20.4 version. Spigot added a non null DamageSource parameter in all EntityDamageEvent constructors, + * and removed existing constructors that did not contain a DamageSource parameter. + * @ScheduledForRemoval Existing until proper support is added to ExprLastDamageCause for DamageSource + */ + @Deprecated + @ScheduledForRemoval + public static boolean DAMAGE_SOURCE; + @Nullable - private static final Constructor OLD_DAMAGE_EVENT_CONSTRUCTOR; + private static Constructor DAMAGE_EVENT_CONSTRUCTOR; static { - Constructor constructor = null; - try { - constructor = EntityDamageEvent.class.getConstructor(Damageable.class, DamageCause.class, double.class); - } catch (NoSuchMethodException ignored) { } - OLD_DAMAGE_EVENT_CONSTRUCTOR = constructor; + if (!DAMAGE_SOURCE) { + try { + DAMAGE_EVENT_CONSTRUCTOR = EntityDamageEvent.class.getConstructor(Damageable.class, DamageCause.class, double.class); + // Throws here. DAMAGE_SOURCE should only be set if DAMAGE_EVENT_CONSTRUCTOR doesn't exist. + DAMAGE_SOURCE = Skript.classExists("org.bukkit.damage.DamageSource"); + } catch (NoSuchMethodException | SecurityException e) {} + } } - public static void setDamageCause(Damageable e, DamageCause cause) { - if (OLD_DAMAGE_EVENT_CONSTRUCTOR != null) { - try { - e.setLastDamageCause(OLD_DAMAGE_EVENT_CONSTRUCTOR.newInstance(e, cause, 0)); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { - Skript.exception("Failed to set last damage cause"); - } - } else { - e.setLastDamageCause(new EntityDamageEvent(e, cause, DamageSource.builder(DamageType.GENERIC).build(), 0)); + /** + * Used to set the damage cause of a damageable entity in an entity damage event. + * Can only be used in versions below 1.20.4. + * + * @param entity The damaged entity. + * @param cause The damage cause in the damage event. + * @deprecated Only used in versions below 1.20.4. See {@link org.bukkit.damage.DamageSource} + */ + @Deprecated + @ScheduledForRemoval + public static void setDamageCause(Damageable entity, DamageCause cause) { + if (DAMAGE_SOURCE) + return; + try { + entity.setLastDamageCause(DAMAGE_EVENT_CONSTRUCTOR.newInstance(entity, cause, 0)); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + Skript.exception(e, "Failed to set last damage cause"); } } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 25000a2390d..d419e4d0582 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Map.Entry; import java.util.UUID; import java.util.regex.Matcher; @@ -37,7 +38,9 @@ import org.bukkit.GameRule; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.OfflinePlayer; +import org.bukkit.Registry; import org.bukkit.SoundCategory; import org.bukkit.World; import org.bukkit.World.Environment; @@ -48,6 +51,8 @@ import org.bukkit.block.DoubleChest; import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; +import org.bukkit.damage.DamageSource; +import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentOffer; import org.bukkit.entity.Cat; @@ -1548,6 +1553,48 @@ public String toVariableNameString(EnchantmentOffer eo) { .name("Transform Reason") .description("Represents a transform reason of an entity transform event.") .since("2.8.0")); + + if (Skript.classExists("org.bukkit.damage.DamageType")) { + Classes.registerClass(new ClassInfo<>(DamageSource.class, "damagesource") + .user("damage ?sources?") + .name("Damage Source") + .description("The damage source in an entity damage event.") + .since("INSERT VERSION")); + + Classes.registerClass(new ClassInfo<>(DamageType.class, "damagetype") + .user("damage ?types?") + .name("Damage Type") + .description("The damage type of a damage source.") + .since("INSERT VERSION") + .parser(new Parser() { + @Override + @Nullable + public DamageType parse(String input, ParseContext context) { + if (input.contains(":")) { + String[] split = input.split(Pattern.quote(":")); + try { + return Registry.DAMAGE_TYPE.get(new NamespacedKey(split[0], split[1])); + } catch (IllegalArgumentException e) {} + } + try { + return Registry.DAMAGE_TYPE.get(NamespacedKey.minecraft(input)); + } catch (IllegalArgumentException e) {} + + return null; + } + + @Override + public String toString(DamageType type, int flags) { + return type.getKey().toString(); + } + + @Override + public String toVariableNameString(DamageType type) { + return "damage type: " + type.getKey().toString(); + } + })); + } + } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprLastDamageCause.java b/src/main/java/ch/njol/skript/expressions/ExprLastDamageCause.java index 520d6198a4f..ea49626bbf7 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLastDamageCause.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLastDamageCause.java @@ -18,11 +18,13 @@ */ package ch.njol.skript.expressions; +import ch.njol.skript.Skript; import ch.njol.skript.bukkitutil.HealthUtils; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.lang.Expression; @@ -30,74 +32,85 @@ import ch.njol.skript.util.Getter; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; + +import org.bukkit.damage.DamageType; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.eclipse.jdt.annotation.Nullable; -/** - * @author bensku - * - */ -@Name("Last Damage Cause") -@Description("Cause of last damage done to an entity") -@Examples({"set last damage cause of event-entity to fire tick"}) +@Name("Last Damage Cause/Type") +@Description({ + "Cause of last damage done to an entity.", + "Using 'damage type' is more accurate as it includes data pack types, but it is only available on versions 1.20.4 and newer." +}) +@Examples({ + "set last damage cause of event-entity to fire tick", + "set last damage type of event-entity to wither" +}) +@RequiredPlugins("Spigot 1.20.4+ for damage type") @Since("2.2-Fixes-V10") -public class ExprLastDamageCause extends PropertyExpression{ - +public class ExprLastDamageCause extends PropertyExpression { + + private static final boolean DAMAGE_TYPE = Skript.classExists("org.bukkit.damage.DamageType"); + static { - register(ExprLastDamageCause.class, DamageCause.class, "last damage (cause|reason|type)", "livingentities"); + register(ExprLastDamageCause.class, Object.class, "last damage (cause|reason|:type)", "livingentities"); } - @SuppressWarnings({"unchecked", "null"}) + private boolean type; + @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { setExpr((Expression) exprs[0]); + type = parseResult.hasTag("type"); return true; } - + @Override - protected DamageCause[] get(Event e, LivingEntity[] source) { - return get(source, new Getter() { + protected Object[] get(Event event, LivingEntity[] source) { + return get(source, new Getter() { @Override - public DamageCause get(LivingEntity entity) { - EntityDamageEvent dmgEvt = entity.getLastDamageCause(); - if (dmgEvt == null) return DamageCause.CUSTOM; - return dmgEvt.getCause(); + public Object get(LivingEntity entity) { + EntityDamageEvent damageEvent = entity.getLastDamageCause(); + if (damageEvent == null) { + if (type && DAMAGE_TYPE) + return DamageType.GENERIC; + return DamageCause.CUSTOM; + } + if (type && DAMAGE_TYPE) + return damageEvent.getDamageSource().getDamageType(); + return damageEvent.getCause(); } }); } - - @Override - public String toString(@Nullable Event e, boolean debug) { - return "the damage cause " + getExpr().toString(e, debug); - } - + @Override @Nullable public Class[] acceptChange(ChangeMode mode) { - if (mode == ChangeMode.REMOVE_ALL) + if (mode == ChangeMode.REMOVE_ALL || type || HealthUtils.DAMAGE_SOURCE) return null; return CollectionUtils.array(DamageCause.class); } - + @Override - public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { - DamageCause d = delta == null ? DamageCause.CUSTOM : (DamageCause) delta[0]; - assert d != null; + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + DamageCause cause = delta == null ? DamageCause.CUSTOM : (DamageCause) delta[0]; + assert cause != null; switch (mode) { case DELETE: case RESET: // Reset damage cause? Umm, maybe it is custom. - for (LivingEntity entity : getExpr().getArray(e)) { + for (LivingEntity entity : getExpr().getArray(event)) { assert entity != null : getExpr(); HealthUtils.setDamageCause(entity, DamageCause.CUSTOM); } break; case SET: - for (LivingEntity entity : getExpr().getArray(e)) { + for (LivingEntity entity : getExpr().getArray(event)) { assert entity != null : getExpr(); - HealthUtils.setDamageCause(entity, d); + HealthUtils.setDamageCause(entity, cause); } break; case REMOVE_ALL: @@ -107,10 +120,17 @@ public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { break; } } - + + @Override + public Class getReturnType() { + return type && DAMAGE_TYPE ? DamageType.class : DamageCause.class; + } + @Override - public Class getReturnType() { - return DamageCause.class; + public String toString(@Nullable Event event, boolean debug) { + if (type && DAMAGE_TYPE) + return "damage type " + getExpr().toString(event, debug); + return "damage cause " + getExpr().toString(event, debug); } } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 058df6dd88c..3503d648530 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -2068,6 +2068,8 @@ types: quitreason: quit reason¦s @a inventoryclosereason: inventory close reason¦s @an transformreason: transform reason¦s @a + damagesource: damage source¦s @a + damagetype: damage type¦s @a # Skript weathertype: weather type¦s @a