diff --git a/src/main/java/ch/njol/skript/effects/EffExplosion.java b/src/main/java/ch/njol/skript/effects/EffExplosion.java index 93c413aa47d..d9caf9851c5 100644 --- a/src/main/java/ch/njol/skript/effects/EffExplosion.java +++ b/src/main/java/ch/njol/skript/effects/EffExplosion.java @@ -1,9 +1,5 @@ package ch.njol.skript.effects; -import org.bukkit.Location; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; @@ -12,70 +8,125 @@ import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.registrations.EventValues; import ch.njol.skript.util.Direction; import ch.njol.util.Kleenean; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -/** - * @author Peter Güttinger - */ @Name("Explosion") -@Description({"Creates an explosion of a given force. The Minecraft Wiki has an article on explosions " + - "which lists the explosion forces of TNT, creepers, etc.", - "Hint: use a force of 0 to create a fake explosion that does no damage whatsoever, or use the explosion effect introduced in Skript 2.0.", - "Starting with Bukkit 1.4.5 and Skript 2.0 you can use safe explosions which will damage entities but won't destroy any blocks."}) -@Examples({"create an explosion of force 10 at the player", - "create an explosion of force 0 at the victim"}) +@Description({ + "Creates an explosion of a given force. The Minecraft Wiki has an article on explosions " + + "which lists the explosion forces of TNT, creepers, etc.", + "Use a force of 0 to create a fake explosion that does no damage whatsoever, or use the 'fake explosion' effect.", + "Use safe explosions to create an explosion which damages entities but won't destroy any blocks."}) +@Examples({ + "create an explosion of force 10 at the player with fire", + "create a safe explosion with force 10", + "create a fake explosion at the player", +}) @Since("1.0") public class EffExplosion extends Effect { static { Skript.registerEffect(EffExplosion.class, - "[(create|make)] [an] explosion (of|with) (force|strength|power) %number% [%directions% %locations%] [(1¦with fire)]", - "[(create|make)] [a] safe explosion (of|with) (force|strength|power) %number% [%directions% %locations%]", - "[(create|make)] [a] fake explosion [%directions% %locations%]", - "[(create|make)] [an] explosion[ ]effect [%directions% %locations%]"); + "[create|make] [an] explosion (of|with) (force|strength|power) %number% [%directions% %locations%] [fire:with fire]", + "[create|make] [a] safe explosion (of|with) (force|strength|power) %number% [%directions% %locations%]", + "[create|make] [a] fake explosion [%directions% %locations%]", + "[create|make] [an] explosion[ ]effect [%directions% %locations%]"); + + EventValues.registerEventValue(ScriptExplodeEvent.class, Location.class, ScriptExplodeEvent::getLocation); + EventValues.registerEventValue(ScriptExplodeEvent.class, Number.class, ScriptExplodeEvent::getPower); } - @Nullable private Expression force; - @SuppressWarnings("null") private Expression locations; private boolean blockDamage; - private boolean setFire; - @SuppressWarnings({"unchecked", "null"}) @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parser) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { + //noinspection unchecked force = matchedPattern <= 1 ? (Expression) exprs[0] : null; blockDamage = matchedPattern != 1; - setFire = parser.mark == 1; - locations = Direction.combine((Expression) exprs[exprs.length - 2], (Expression) exprs[exprs.length - 1]); + setFire = parser.hasTag("fire"); + //noinspection unchecked + locations = Direction.combine((Expression) exprs[exprs.length - 2], + (Expression) exprs[exprs.length - 1]); return true; } @Override - public void execute(final Event e) { - final Number power = force != null ? force.getSingle(e) : 0; - if (power == null) + public void execute(Event event) { + Number optionalForce = force != null ? force.getSingle(event) : 0; + if (optionalForce == null) return; - for (Location location : locations.getArray(e)) { + + float power = optionalForce.floatValue(); + + for (Location location : locations.getArray(event)) { if (location.getWorld() == null) continue; - if (!blockDamage) - location.getWorld().createExplosion(location.getX(), location.getY(), location.getZ(), power.floatValue(), false, false); - else - location.getWorld().createExplosion(location, power.floatValue(), setFire); + + boolean cancelled; + if (!blockDamage) { + cancelled = location.getWorld().createExplosion(location.getX(), location.getY(), location.getZ(), + power, false, false); + } else { + cancelled = location.getWorld().createExplosion(location, power, setFire); + } + + if (!cancelled) + Bukkit.getPluginManager().callEvent(new ScriptExplodeEvent(location, power)); } } @Override - public String toString(final @Nullable Event e, final boolean debug) { - if (force != null) - return "create explosion of force " + force.toString(e, debug) + " " + locations.toString(e, debug); - else - return "create explosion effect " + locations.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + if (force != null) { + return "create explosion of force " + force.toString(event, debug) + " " + locations.toString(event, debug); + } else { + return "create explosion effect " + locations.toString(event, debug); + } + } + + /** + * Event for handling explosions created by this effect. + */ + public static class ScriptExplodeEvent extends Event { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Location at; + private final float power; + + public ScriptExplodeEvent(@NotNull Location at, float power) { + this.at = at; + this.power = power; + } + + public @NotNull Location getLocation() { + return at; + } + + public float getPower() { + return power; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + } } diff --git a/src/main/java/ch/njol/skript/events/EvtExplode.java b/src/main/java/ch/njol/skript/events/EvtExplode.java new file mode 100644 index 00000000000..869096a8bcd --- /dev/null +++ b/src/main/java/ch/njol/skript/events/EvtExplode.java @@ -0,0 +1,177 @@ +package ch.njol.skript.events; + +import ch.njol.skript.Skript; +import ch.njol.skript.effects.EffExplosion; +import ch.njol.skript.entity.EntityType; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptEvent; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.util.Color; +import ch.njol.skript.util.ColorRGB; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.FireworkEffect; +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.FireworkExplodeEvent; +import org.bukkit.inventory.meta.FireworkMeta; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; +import java.util.stream.Collectors; + +public class EvtExplode extends SkriptEvent { + + static { + Skript.registerEvent("Explode", EvtExplode.class, + CollectionUtils.array(EffExplosion.ScriptExplodeEvent.class, FireworkExplodeEvent.class, EntityExplodeEvent.class), + "[a] script[ed] explo(d(e|ing)|sion)", + "[a] firework explo(d(e|ing)|sion) [colo[u]red %-colors%]", + "[a] [%entitytypes%] explo(d(e|ing)|sion)" + ) + .description( + "Called when an entity explodes, or when an explosion is created by a script.", + "Entity and script explosions have a power value, obtained by using `event-number`.", + "Fireworks have an optional specifier for the exploded color." + ) + .examples( + "on explosion:", + "on script explosion:", + "on tnt explosion:", + "on firework explode:", + "\tif event-colors contains red:", + "on firework exploding colored red, light green and black:", + "on firework explosion colored rgb 0, 255, 0:", + "\tbroadcast \"A firework colored %colors% was exploded at %location%!\"" + ) + .since("1.0, INSERT VERSION (script)"); + } + + private State state; + + @Override + public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + switch (matchedPattern) { + case 0 -> state = State.SCRIPT; + case 1 -> state = State.FIREWORK; + default -> state = State.ENTITY; + } + + return state.init(args, matchedPattern, parseResult); + } + + @Override + public boolean check(Event event) { + return state.check(event); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return state.toString(event, debug); + } + + private enum State { + + SCRIPT { + @Override + boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + return true; + } + + @Override + boolean check(Event event) { + return event instanceof EffExplosion.ScriptExplodeEvent; + } + + @Override + String toString(@Nullable Event event, boolean debug) { + return "script explosion"; + } + }, + FIREWORK { + private @Nullable Literal colors; + + @Override + boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + if (args[0] != null) + //noinspection unchecked + colors = (Literal) args[0]; + return true; + } + + @Override + boolean check(Event event) { + if (!(event instanceof FireworkExplodeEvent fireworkExplodeEvent)) + return false; + + if (colors == null) + return true; + + Set colours = colors.stream(event) + .map(color -> { + if (color instanceof ColorRGB) + return color.asBukkitColor(); + return color.asDyeColor().getFireworkColor(); + }) + .collect(Collectors.toSet()); + + FireworkMeta meta = fireworkExplodeEvent.getEntity().getFireworkMeta(); + for (FireworkEffect effect : meta.getEffects()) { + if (colours.containsAll(effect.getColors())) + return true; + } + return false; + } + + @Override + String toString(@Nullable Event event, boolean debug) { + return "firework explode " + (colors != null ? " with colors " + colors.toString(event, debug) : ""); + } + }, + ENTITY { + private @Nullable Literal typesLiteral; + private EntityType @Nullable [] types; + + @Override + boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + //noinspection unchecked + Literal arg = (Literal) args[0]; + if (arg == null) + return false; + + typesLiteral = arg; + types = arg.getAll(); + return true; + } + + @Override + boolean check(Event event) { + if (!(event instanceof EntityExplodeEvent explodeEvent)) + return false; + if (types == null) + return true; + + for (EntityType type : types) { + if (type.isInstance(explodeEvent.getEntity())) + return true; + } + return false; + } + + @Override + String toString(@Nullable Event event, boolean debug) { + if (typesLiteral != null) + return typesLiteral.toString(event, debug) + " explosion"; + + return "explosion"; + } + }; + + abstract boolean init(Literal[] args, int matchedPattern, ParseResult parseResult); + + abstract boolean check(Event event); + + abstract String toString(@Nullable Event event, boolean debug); + + } + +} diff --git a/src/main/java/ch/njol/skript/events/EvtFirework.java b/src/main/java/ch/njol/skript/events/EvtFirework.java deleted file mode 100644 index 7c64987f0de..00000000000 --- a/src/main/java/ch/njol/skript/events/EvtFirework.java +++ /dev/null @@ -1,72 +0,0 @@ -package ch.njol.skript.events; - -import ch.njol.skript.Skript; -import ch.njol.skript.lang.Literal; -import ch.njol.skript.lang.SkriptEvent; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.util.Color; -import java.util.Arrays; -import java.util.List; - -import ch.njol.skript.util.ColorRGB; -import org.bukkit.FireworkEffect; -import org.bukkit.event.Event; -import org.bukkit.event.entity.FireworkExplodeEvent; -import org.bukkit.inventory.meta.FireworkMeta; -import org.jetbrains.annotations.Nullable; - - -public class EvtFirework extends SkriptEvent { - - static { - if (Skript.classExists("org.bukkit.event.entity.FireworkExplodeEvent")) - //Making the event argument type fireworkeffects, led to Skript having troubles parsing for some reason. - Skript.registerEvent("Firework Explode", EvtFirework.class, FireworkExplodeEvent.class, "[a] firework explo(d(e|ing)|sion) [colo[u]red %-colors%]") - .description("Called when a firework explodes.") - .examples("on firework explode:", - "\tif event-colors contains red:", - "on firework exploding colored red, light green and black:", - "on firework explosion colored rgb 0, 255, 0:", - "\tbroadcast \"A firework colored %colors% was exploded at %location%!\"") - .since("2.4"); - } - - private @Nullable Literal colors; - - @SuppressWarnings("unchecked") - @Override - public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { - if (args[0] != null) - colors = (Literal) args[0]; - return true; - } - - @Override - public boolean check(Event event) { - if (!(event instanceof FireworkExplodeEvent fireworkExplodeEvent)) - return false; - - if (colors == null) - return true; - - List colours = colors.stream(event) - .map(color -> { - if (color instanceof ColorRGB) - return color.asBukkitColor(); - return color.asDyeColor().getFireworkColor(); - }) - .toList(); - FireworkMeta meta = fireworkExplodeEvent.getEntity().getFireworkMeta(); - for (FireworkEffect effect : meta.getEffects()) { - if (colours.containsAll(effect.getColors())) - return true; - } - return false; - } - - @Override - public String toString(@Nullable Event e, boolean debug) { - return "Firework explode " + (colors != null ? " with colors " + colors.toString(e, debug) : ""); - } - -} diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index 9997a21405b..4c996975f17 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -111,10 +111,6 @@ public class SimpleEvents { .description("Called when an entity is set on fire, e.g. by fire or lava, a fireball, or by standing in direct sunlight (zombies, skeletons).") .examples("on combust:") .since("1.0"); - Skript.registerEvent("Explode", SimpleEvent.class, EntityExplodeEvent.class, "explo(d(e|ing)|sion)") - .description("Called when an entity (a primed TNT or a creeper) explodes.") - .examples("on explosion:") - .since("1.0"); // Skript.registerEvent(SimpleEvent.class, EntityInteractEvent.class, "interact");// = entity interacts with block, e.g. endermen?; player -> PlayerInteractEvent // likely tripwires, pressure plates, etc. Skript.registerEvent("Portal Enter", SimpleEvent.class, EntityPortalEnterEvent.class, "portal enter[ing]", "entering [a] portal") .description("Called when an entity enters a nether portal or an end portal. Please note that this event will be fired many times for a nether portal.") diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtFireworkTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtExplodeTest.java similarity index 71% rename from src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtFireworkTest.java rename to src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtExplodeTest.java index 75941ff7de6..953327f64e2 100644 --- a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtFireworkTest.java +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtExplodeTest.java @@ -1,13 +1,17 @@ package org.skriptlang.skript.test.tests.syntaxes.events; import ch.njol.skript.Skript; +import ch.njol.skript.effects.EffExplosion; import ch.njol.skript.test.runner.SkriptJUnitTest; import ch.njol.skript.util.SkriptColor; import org.bukkit.Bukkit; +import org.bukkit.ExplosionResult; import org.bukkit.FireworkEffect; import org.bukkit.entity.EntityType; import org.bukkit.entity.Firework; +import org.bukkit.entity.Pig; import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.FireworkExplodeEvent; import org.bukkit.inventory.meta.FireworkMeta; import org.junit.After; @@ -17,22 +21,26 @@ import java.util.ArrayList; import java.util.List; -public class EvtFireworkTest extends SkriptJUnitTest { +public class EvtExplodeTest extends SkriptJUnitTest { + + private Pig pig; private EntityType entityType; - private List fireworkList = new ArrayList<>(); + private final List fireworkList = new ArrayList<>(); @Before - public void getEntity() { + public void before() { if (Skript.isRunningMinecraft(1, 20, 5)) { entityType = EntityType.FIREWORK_ROCKET; } else { entityType = EntityType.valueOf("FIREWORK"); } + + pig = spawnTestPig(); } @Test - public void callEvents() { + public void test() { List events = new ArrayList<>(); for (SkriptColor color : SkriptColor.values()) { Firework firework = (Firework) getTestWorld().spawnEntity(getTestLocation(), entityType); @@ -44,13 +52,19 @@ public void callEvents() { events.add(new FireworkExplodeEvent(firework)); } + events.add(new EffExplosion.ScriptExplodeEvent(getTestLocation(), 10)); + events.add(new EntityExplodeEvent(pig, getTestLocation(), List.of(), 10, + ExplosionResult.DESTROY_WITH_DECAY)); + for (Event event : events) { Bukkit.getPluginManager().callEvent(event); } } @After - public void cleanUp() { + public void after() { + pig.remove(); + for (Firework firework : fireworkList) { firework.remove(); } diff --git a/src/test/skript/junit/EvtExplodeTest.sk b/src/test/skript/junit/EvtExplodeTest.sk new file mode 100644 index 00000000000..6e821ce57ca --- /dev/null +++ b/src/test/skript/junit/EvtExplodeTest.sk @@ -0,0 +1,32 @@ +options: + EvtExplodeTest: "org.skriptlang.skript.test.tests.syntaxes.events.EvtExplodeTest" + +test "EvtExplodeJUnit" when running JUnit: + set {_tests::1} to "any firework" + loop all colors: + set {_tests::%loop-iteration + 1%} to "%loop-color% firework" + + add "script explode" to {_tests::*} + add "entity explode" to {_tests::*} + + ensure junit test {@EvtExplodeTest} completes {_tests::*} + +on scripted explode: + junit test is {@EvtExplodeTest} + + if event-location is set: + complete objective "script explode" for {@EvtExplodeTest} + +on pig explode: + junit test is {@EvtExplodeTest} + + if event-entity is pig: + complete objective "entity explode" for {@EvtExplodeTest} + +on firework explode: + junit test is {@EvtExplodeTest} + + complete objective "any firework" for {@EvtExplodeTest} + if event-colors is set: + set {_color} to first element of event-colors + complete objective "%{_color}% firework" for {@EvtExplodeTest} \ No newline at end of file diff --git a/src/test/skript/junit/EvtFireworkTest.sk b/src/test/skript/junit/EvtFireworkTest.sk deleted file mode 100644 index 9646a81a82d..00000000000 --- a/src/test/skript/junit/EvtFireworkTest.sk +++ /dev/null @@ -1,16 +0,0 @@ -options: - EvtFireworkTest: "org.skriptlang.skript.test.tests.syntaxes.events.EvtFireworkTest" - -test "EvtFireworkJUnit" when running JUnit: - set {_tests::1} to "any firework" - loop all colors: - set {_tests::%loop-iteration + 1%} to "%loop-color% firework" - - ensure junit test {@EvtFireworkTest} completes {_tests::*} - -on firework explode: - junit test is {@EvtFireworkTest} - complete objective "any firework" for {@EvtFireworkTest} - if event-colors is set: - set {_color} to first element of event-colors - complete objective "%{_color}% firework" for {@EvtFireworkTest}