From a5f0f4fc3bbe8633f154f12f1842734869dfcad3 Mon Sep 17 00:00:00 2001 From: sovdee Date: Fri, 23 Jun 2023 19:53:05 -0700 Subject: [PATCH 01/12] Adds EffToggleCanPickUpItems + CondCanPickUpItems (#5357) --- .../skript/conditions/CondCanPickUpItems.java | 60 ++++++++++++++ .../effects/EffToggleCanPickUpItems.java | 79 +++++++++++++++++++ .../effects/EffToggleCanPickUpItems.sk | 7 ++ 3 files changed, 146 insertions(+) create mode 100644 src/main/java/ch/njol/skript/conditions/CondCanPickUpItems.java create mode 100644 src/main/java/ch/njol/skript/effects/EffToggleCanPickUpItems.java create mode 100644 src/test/skript/tests/syntaxes/effects/EffToggleCanPickUpItems.sk diff --git a/src/main/java/ch/njol/skript/conditions/CondCanPickUpItems.java b/src/main/java/ch/njol/skript/conditions/CondCanPickUpItems.java new file mode 100644 index 00000000000..d5a0aaaa591 --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondCanPickUpItems.java @@ -0,0 +1,60 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.conditions; + +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import org.bukkit.entity.LivingEntity; + +@Name("Can Pick Up Items") +@Description("Whether living entities are able to pick up items off the ground or not.") +@Examples({ + "if player can pick items up:", + "\tsend \"You can pick up items!\" to player", + "", + "on drop:", + "\tif player can't pick up items:", + "\t\tsend \"Be careful, you won't be able to pick that up!\" to player" +}) +@Since("INSERT VERSION") +public class CondCanPickUpItems extends PropertyCondition { + + static { + register(CondCanPickUpItems.class, PropertyType.CAN, "pick([ ]up items| items up)", "livingentities"); + } + + @Override + public boolean check(LivingEntity livingEntity) { + return livingEntity.getCanPickupItems(); + } + + @Override + protected PropertyType getPropertyType() { + return PropertyType.CAN; + } + + @Override + protected String getPropertyName() { + return "pick up items"; + } + +} diff --git a/src/main/java/ch/njol/skript/effects/EffToggleCanPickUpItems.java b/src/main/java/ch/njol/skript/effects/EffToggleCanPickUpItems.java new file mode 100644 index 00000000000..6c3b3926988 --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffToggleCanPickUpItems.java @@ -0,0 +1,79 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Toggle Picking Up Items") +@Description("Determines whether living entities are able to pick up items or not") +@Examples({ + "forbid player from picking up items", + "send \"You can no longer pick up items!\" to player", + "", + "on drop:", + "\tif player can't pick up items:", + "\t\tallow player to pick up items" +}) +@Since("INSERT VERSION") +public class EffToggleCanPickUpItems extends Effect { + + static { + Skript.registerEffect(EffToggleCanPickUpItems.class, + "allow %livingentities% to pick([ ]up items| items up)", + "(forbid|disallow) %livingentities% (from|to) pick([ing | ]up items|[ing] items up)"); + } + + private Expression entities; + private boolean allowPickUp; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + entities = (Expression) exprs[0]; + allowPickUp = matchedPattern == 0; + return true; + } + + @Override + protected void execute(Event event) { + for (LivingEntity entity : entities.getArray(event)) { + entity.setCanPickupItems(allowPickUp); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (allowPickUp) { + return "allow " + entities.toString(event, debug) + " to pick up items"; + } else { + return "forbid " + entities.toString(event, debug) + " from picking up items"; + } + } + +} diff --git a/src/test/skript/tests/syntaxes/effects/EffToggleCanPickUpItems.sk b/src/test/skript/tests/syntaxes/effects/EffToggleCanPickUpItems.sk new file mode 100644 index 00000000000..308a07261b3 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffToggleCanPickUpItems.sk @@ -0,0 +1,7 @@ +test "entity can pick up items": + spawn a zombie at spawn of "world": + allow event-entity to pick up items + assert event-entity can pick up items with "failed to allow zombie to pick up items" + forbid event-entity from picking items up + assert event-entity can't pick up items with "failed to disallow zombie to pick up items" + delete event-entity From 1949a237ce4ffe7137896496864f033d614efae4 Mon Sep 17 00:00:00 2001 From: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Date: Sat, 24 Jun 2023 23:43:23 +0300 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=9A=80=20Add=20anvil=20repair=20cos?= =?UTF-8?q?t=20expression=20(#4617)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expressions/ExprAnvilRepairCost.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprAnvilRepairCost.java diff --git a/src/main/java/ch/njol/skript/expressions/ExprAnvilRepairCost.java b/src/main/java/ch/njol/skript/expressions/ExprAnvilRepairCost.java new file mode 100644 index 00000000000..dd47b9af14f --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprAnvilRepairCost.java @@ -0,0 +1,119 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +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.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Anvil Repair Cost") +@Description({ + "Returns the experience cost (in levels) to complete the current repair or the maximum experience cost (in levels) to be allowed by the current repair.", + "The default value of max cost set by vanilla Minecraft is 40." +}) +@Examples({ + "on inventory click:", + "\tif {AnvilRepairSaleActive} = true:", + "\t\twait a tick # recommended, to avoid client bugs", + "\t\tset anvil repair cost to anvil repair cost * 50%", + "\t\tsend \"Anvil repair sale is ON!\" to player", + "", + "on inventory click:", + "\tplayer have permission \"anvil.repair.max.bypass\"", + "\tset max repair cost of event-inventory to 99999" +}) +@Since("INSERT VERSION") +public class ExprAnvilRepairCost extends SimplePropertyExpression { + + static { + registerDefault(ExprAnvilRepairCost.class, Integer.class, "[anvil] [item] [:max[imum]] repair cost", "inventories"); + } + + private boolean isMax; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + isMax = parseResult.hasTag("max"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + @Nullable + public Integer convert(Inventory inventory) { + if (!(inventory instanceof AnvilInventory)) + return null; + + AnvilInventory anvilInventory = (AnvilInventory) inventory; + return isMax ? anvilInventory.getMaximumRepairCost() : anvilInventory.getRepairCost(); + } + + @Override + @Nullable + public Class[] acceptChange(ChangeMode mode) { + switch (mode) { + case ADD: + case REMOVE: + case SET: + return CollectionUtils.array(Number.class); + default: + return null; + } + } + + @Override + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + int value = ((Number) delta[0]).intValue() * (mode == ChangeMode.REMOVE ? -1 : 1); + for (Inventory inventory : getExpr().getArray(event)) { + if (inventory instanceof AnvilInventory) { + AnvilInventory anvilInventory = (AnvilInventory) inventory; + int change = mode == ChangeMode.SET ? 0 : (isMax ? anvilInventory.getMaximumRepairCost() : anvilInventory.getRepairCost()); + int newValue = Math.max((change + value), 0); + + if (isMax) + anvilInventory.setMaximumRepairCost(newValue); + else + anvilInventory.setRepairCost(newValue); + } + } + } + + @Override + public Class getReturnType() { + return Integer.class; + } + + @Override + public String getPropertyName() { + return "anvil item" + (isMax ? " max" : "") + " repair cost"; + } + +} From 302f8f9ac7cac1d1a1a68ce85cb75fad4fafd4a1 Mon Sep 17 00:00:00 2001 From: sovdee Date: Sun, 25 Jun 2023 00:01:30 -0700 Subject: [PATCH 03/12] Adds Clamp Function (#5573) --- .../skript/classes/data/DefaultFunctions.java | 53 ++++++++++++++----- .../skript/tests/syntaxes/functions/clamp.sk | 43 +++++++++++++++ 2 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/functions/clamp.sk diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index aeb0fe4735b..eee9206b0a0 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -18,19 +18,10 @@ */ package ch.njol.skript.classes.data; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.Calendar; - -import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.JavaFunction; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.util.Vector; - import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.lang.function.FunctionEvent; import ch.njol.skript.lang.function.Functions; +import ch.njol.skript.lang.function.JavaFunction; import ch.njol.skript.lang.function.Parameter; import ch.njol.skript.lang.function.SimpleJavaFunction; import ch.njol.skript.lang.util.SimpleLiteral; @@ -41,8 +32,16 @@ import ch.njol.util.Math2; import ch.njol.util.StringUtils; import ch.njol.util.coll.CollectionUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Calendar; + public class DefaultFunctions { private static String str(double n) { @@ -305,7 +304,37 @@ public Number[] executeSimple(Object[][] params) { }.description("Returns the minimum number from a list of numbers.") .examples("min(1) = 1", "min(1, 2, 3, 4) = 1", "min({some list variable::*})") .since("2.2")); - + + Functions.registerFunction(new SimpleJavaFunction("clamp", new Parameter[]{ + new Parameter<>("values", DefaultClasses.NUMBER, false, null), + new Parameter<>("min", DefaultClasses.NUMBER, true, null), + new Parameter<>("max", DefaultClasses.NUMBER, true, null) + }, DefaultClasses.NUMBER, false) { + @Override + public @Nullable Number[] executeSimple(Object[][] params) { + Number[] values = (Number[]) params[0]; + Double[] clampedValues = new Double[values.length]; + double min = ((Number) params[1][0]).doubleValue(); + double max = ((Number) params[2][0]).doubleValue(); + // we'll be nice and swap them if they're in the wrong order + double trueMin = Math.min(min, max); + double trueMax = Math.max(min, max); + for (int i = 0; i < values.length; i++) { + double value = values[i].doubleValue(); + clampedValues[i] = Math.max(Math.min(value, trueMax), trueMin); + } + return clampedValues; + } + }).description("Clamps one or more values between two numbers.") + .examples( + "clamp(5, 0, 10) = 5", + "clamp(5.5, 0, 5) = 5", + "clamp(0.25, 0, 0.5) = 0.25", + "clamp(5, 7, 10) = 7", + "clamp((5, 0, 10, 9, 13), 7, 10) = (7, 7, 10, 9, 10)", + "set {_clamped::*} to clamp({_values::*}, 0, 10)") + .since("INSERT VERSION"); + // misc Functions.registerFunction(new SimpleJavaFunction("world", new Parameter[] { diff --git a/src/test/skript/tests/syntaxes/functions/clamp.sk b/src/test/skript/tests/syntaxes/functions/clamp.sk new file mode 100644 index 00000000000..98b9ad85dc5 --- /dev/null +++ b/src/test/skript/tests/syntaxes/functions/clamp.sk @@ -0,0 +1,43 @@ +test "clamp numbers": + # Normal Cases + assert clamp(1, 0, 2) is 1 with "(single ints) min < value < max" + assert clamp(1, 1, 2) is 1 with "(single ints) min = value < max" + assert clamp(2, 1, 2) is 2 with "(single ints) min < value = max" + assert clamp(0, 1, 2) is 1 with "(single ints) value < min < max" + assert clamp(3, 1, 2) is 2 with "(single ints) min < max < value" + assert clamp(3, 2, 1) is 2 with "(single ints) max < min < value" + + assert clamp(1.999, 0.0, 2.0) is 1.999 with "(single floats) min < value < max" + assert clamp(1.999, 1.999, 2.0) is 1.999 with "(single floats) min = value < max" + assert clamp(2.0, 1.999, 2.0) is 2.0 with "(single floats) min < value = max" + assert clamp(0.0, 1.999, 2.0) is 1.999 with "(single floats) value < min < max" + assert clamp(3.0, 1.999, 2.0) is 2.0 with "(single floats) min < max < value" + assert clamp(2.999, 2.0, 1.999) is 2.0 with "(single floats) max < min < value" + + # Lists + set {_expected::*} to (0, 0, 1, 2, 2, and 2) + # this is dumb but comparing the lists directly didn't work + set {_got::*} to clamp((-1, 0, 1, 2, 3, and 4), 0, 2) + loop {_expected::*}: + assert {_got::%loop-index%} is loop-value with "(multiple ints)" + set {_got::*} to clamp((-1.999, 0.0, 1.0, 2.0, 3.0, and 4.0), 0.0, 2.0) + loop {_expected::*}: + assert {_got::%loop-index%} is loop-value with "(multiple floats)" + + # Edge Cases + assert clamp(1, {_null}, 2) is not set with "(single ints) min = null" + assert clamp(2, 1, {_null}) is not set with "(single ints) max = null" + assert clamp({_null}, 1, 2) is not set with "(single ints) value = null" + assert clamp(1, 0, NaN value) is not clamp(1, 0, NaN value) with "(single ints) min < value < NaN" + assert clamp(1, NaN value, 2) is not clamp(1, NaN value, 2) with "(single ints) NaN < value < max" + assert clamp(NaN value, 1, 2) is not clamp(NaN value, 1, 2) with "(single ints) min < NaN < max" + assert clamp(infinity value, 1, 2) is 2 with "(single ints) min < infinity < max" + assert clamp(-infinity value, 1, 2) is 1 with "(single ints) min < -infinity < max" + assert clamp(1, 0, infinity value) is 1 with "(single ints) min < value < infinity" + assert clamp(1, -infinity value, 2) is 1 with "(single ints) -infinity < value < max" + + set {_expected::*} to (NaN value, 0.0, and 2.0) + set {_got::*} to clamp(({_null}, NaN value, -infinity value, infinity value), 0.0, 2.0) + assert number within {_got::1} is not number within {_got::1} with "(edge cases list) NaN" # need within because the variables weren't cooperating + assert {_got::2} is {_expected::2} with "(edge cases list) -infinity" + assert {_got::3} is {_expected::3} with "(edge cases list) infinity" From 2e5dc2ea2361f346dc6b42863f12fc216ef68156 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Jun 2023 02:58:32 -0600 Subject: [PATCH 04/12] Bump io.papermc.paper:paper-api from 1.20-R0.1-SNAPSHOT to 1.20.1-R0.1-SNAPSHOT (#5746) * Bump io.papermc.paper:paper-api Bumps io.papermc.paper:paper-api from 1.20-R0.1-SNAPSHOT to 1.20.1-R0.1-SNAPSHOT. --- updated-dependencies: - dependency-name: io.papermc.paper:paper-api dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update build.gradle * Update gradle.properties * Update and rename paper-1.20.json to paper-1.20.1.json --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> --- build.gradle | 4 ++-- gradle.properties | 2 +- .../java17/{paper-1.20.json => paper-1.20.1.json} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/test/skript/environments/java17/{paper-1.20.json => paper-1.20.1.json} (85%) diff --git a/build.gradle b/build.gradle index f05c899d0b2..5920cb95ac8 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ dependencies { shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.0.2' shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.0' - implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20-R0.1-SNAPSHOT' + implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.1-R0.1-SNAPSHOT' implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700' implementation group: 'com.google.code.findbugs', name: 'findbugs', version: '3.0.1' implementation group: 'com.sk89q.worldguard', name: 'worldguard-legacy', version: '7.0.0-SNAPSHOT' @@ -254,7 +254,7 @@ void createTestTask(String name, String desc, String environments, int javaVersi } } -def latestEnv = 'java17/paper-1.20.json' +def latestEnv = 'java17/paper-1.20.1.json' def latestJava = 17 def oldestJava = 8 diff --git a/gradle.properties b/gradle.properties index 22cf891c6cb..815459f135e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,5 @@ groupid=ch.njol name=skript version=2.7.0-beta3 jarName=Skript.jar -testEnv=java17/paper-1.20 +testEnv=java17/paper-1.20.1 testEnvJavaVersion=17 diff --git a/src/test/skript/environments/java17/paper-1.20.json b/src/test/skript/environments/java17/paper-1.20.1.json similarity index 85% rename from src/test/skript/environments/java17/paper-1.20.json rename to src/test/skript/environments/java17/paper-1.20.1.json index 73c81a4018c..3a117b97397 100644 --- a/src/test/skript/environments/java17/paper-1.20.json +++ b/src/test/skript/environments/java17/paper-1.20.1.json @@ -1,11 +1,11 @@ { - "name": "paper-1.20", + "name": "paper-1.20.1", "resources": [ {"source": "server.properties.generic", "target": "server.properties"} ], "paperDownloads": [ { - "version": "1.20", + "version": "1.20.1", "target": "paperclip.jar" } ], From ce1888c4b5435ff0fa13e5fd8072c8da0c79fc56 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Sun, 25 Jun 2023 18:10:47 -0600 Subject: [PATCH 05/12] Add quit reason (#5763) * Add quit reason * Cleanup * Versioning --- .../skript/classes/data/BukkitClasses.java | 68 ++++++++++-------- .../classes/data/BukkitEventValues.java | 70 +++++++++++-------- .../skript/expressions/ExprQuitReason.java | 69 ++++++++++++++++++ src/main/resources/lang/default.lang | 8 +++ 4 files changed, 157 insertions(+), 58 deletions(-) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprQuitReason.java 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 ccf12240d29..5caa14f02e2 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -62,6 +62,7 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerQuitEvent.QuitReason; import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.inventory.Inventory; @@ -1458,29 +1459,29 @@ public String toVariableNameString(GameRule o) { ); Classes.registerClass(new ClassInfo<>(EnchantmentOffer.class, "enchantmentoffer") - .user("enchant[ment][ ]offers?") - .name("Enchantment Offer") - .description("The enchantmentoffer in an enchant prepare event.") - .examples("on enchant prepare:", - "\tset enchant offer 1 to sharpness 1", - "\tset the cost of enchant offer 1 to 10 levels") - .since("2.5") - .parser(new Parser() { - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(EnchantmentOffer eo, int flags) { - return EnchantmentType.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); - } - - @Override - public String toVariableNameString(EnchantmentOffer eo) { - return "offer:" + EnchantmentType.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); - } - })); + .user("enchant[ment][ ]offers?") + .name("Enchantment Offer") + .description("The enchantmentoffer in an enchant prepare event.") + .examples("on enchant prepare:", + "\tset enchant offer 1 to sharpness 1", + "\tset the cost of enchant offer 1 to 10 levels") + .since("2.5") + .parser(new Parser() { + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(EnchantmentOffer eo, int flags) { + return EnchantmentType.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); + } + + @Override + public String toVariableNameString(EnchantmentOffer eo) { + return "offer:" + EnchantmentType.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); + } + })); Classes.registerClass(new EnumClassInfo<>(Attribute.class, "attributetype", "attribute types") .user("attribute ?types?") @@ -1495,13 +1496,20 @@ public String toVariableNameString(EnchantmentOffer eo) { .description("Represents the environment of a world.") .since("2.7")); - if (Skript.classExists("io.papermc.paper.world.MoonPhase")) { + if (Skript.classExists("io.papermc.paper.world.MoonPhase")) Classes.registerClass(new EnumClassInfo<>(MoonPhase.class, "moonphase", "moon phases") - .user("(lunar|moon) ?phases?") - .name("Moon Phase") - .description("Represents the phase of a moon.") - .since("2.7") - .requiredPlugins("Paper 1.16+")); - } + .user("(lunar|moon) ?phases?") + .name("Moon Phase") + .description("Represents the phase of a moon.") + .requiredPlugins("Paper 1.16+") + .since("2.7")); + + if (Skript.classExists("org.bukkit.event.player.PlayerQuitEvent$QuitReason")) + Classes.registerClass(new EnumClassInfo<>(QuitReason.class, "quitreason", "quit reasons") + .user("(quit|disconnect) ?(reason|cause)s?") + .name("Quit Reason") + .description("Represents a quit reason from a player quit server event.") + .requiredPlugins("Paper 1.16.5+") + .since("INSERT VERSION")); } } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index b628c56a887..9bf77216c51 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -18,40 +18,24 @@ */ package ch.njol.skript.classes.data; -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.Aliases; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.command.CommandEvent; -import ch.njol.skript.events.bukkit.ScriptEvent; -import ch.njol.skript.events.bukkit.SkriptStartEvent; -import ch.njol.skript.events.bukkit.SkriptStopEvent; -import ch.njol.skript.registrations.EventValues; -import ch.njol.skript.util.BlockStateBlock; -import ch.njol.skript.util.BlockUtils; -import ch.njol.skript.util.DelayedChangeBlock; -import ch.njol.skript.util.Direction; -import ch.njol.skript.util.EnchantmentType; -import ch.njol.skript.util.Getter; -import ch.njol.skript.util.slot.InventorySlot; -import ch.njol.skript.util.slot.Slot; -import com.destroystokyo.paper.event.block.AnvilDamagedEvent; -import com.destroystokyo.paper.event.entity.ProjectileCollideEvent; -import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; -import io.papermc.paper.event.entity.EntityMoveEvent; -import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; -import io.papermc.paper.event.player.PlayerTradeEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.FireworkEffect; +import org.bukkit.GameMode; import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.GameMode; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.command.CommandSender; +import org.bukkit.entity.AbstractVillager; import org.bukkit.entity.Egg; import org.bukkit.entity.Entity; import org.bukkit.entity.Firework; @@ -62,7 +46,6 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.entity.Vehicle; -import org.bukkit.entity.AbstractVillager; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockCanBuildEvent; import org.bukkit.event.block.BlockDamageEvent; @@ -127,6 +110,8 @@ import org.bukkit.event.player.PlayerItemMendEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerQuitEvent.QuitReason; import org.bukkit.event.player.PlayerRiptideEvent; import org.bukkit.event.player.PlayerShearEntityEvent; import org.bukkit.event.player.PlayerTeleportEvent; @@ -154,10 +139,29 @@ import org.bukkit.potion.PotionEffectType; import org.eclipse.jdt.annotation.Nullable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import com.destroystokyo.paper.event.block.AnvilDamagedEvent; +import com.destroystokyo.paper.event.entity.ProjectileCollideEvent; +import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; + +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.Aliases; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.command.CommandEvent; +import ch.njol.skript.events.bukkit.ScriptEvent; +import ch.njol.skript.events.bukkit.SkriptStartEvent; +import ch.njol.skript.events.bukkit.SkriptStopEvent; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.util.BlockStateBlock; +import ch.njol.skript.util.BlockUtils; +import ch.njol.skript.util.DelayedChangeBlock; +import ch.njol.skript.util.Direction; +import ch.njol.skript.util.EnchantmentType; +import ch.njol.skript.util.Getter; +import ch.njol.skript.util.slot.InventorySlot; +import ch.njol.skript.util.slot.Slot; +import io.papermc.paper.event.entity.EntityMoveEvent; +import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; +import io.papermc.paper.event.player.PlayerTradeEvent; /** * @author Peter Güttinger @@ -1644,5 +1648,15 @@ public Location get(LootGenerateEvent event) { } }, EventValues.TIME_NOW); } + + //PlayerQuitEvent + if (Skript.classExists("org.bukkit.event.player.PlayerQuitEvent$QuitReason")) + EventValues.registerEventValue(PlayerQuitEvent.class, QuitReason.class, new Getter() { + @Override + @Nullable + public QuitReason get(PlayerQuitEvent event) { + return event.getReason(); + } + }, EventValues.TIME_NOW); } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprQuitReason.java b/src/main/java/ch/njol/skript/expressions/ExprQuitReason.java new file mode 100644 index 00000000000..6d5fd5cf751 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprQuitReason.java @@ -0,0 +1,69 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerQuitEvent.QuitReason; +import org.eclipse.jdt.annotation.Nullable; + +import ch.njol.skript.Skript; +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.EventValueExpression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.registrations.EventValues; + +@Name("Quit Reason") +@Description("The quit reason as to why a player disconnected in a quit event.") +@Examples({ + "on quit:", + "\tquit reason was kicked", + "\tplayer is banned", + "\tclear {server::player::%uuid of player%::*}" +}) +@RequiredPlugins("Paper 1.16.5+") +@Since("INSERT VERSION") +public class ExprQuitReason extends EventValueExpression { + + static { + if (Skript.classExists("org.bukkit.event.player.PlayerQuitEvent$QuitReason")) + Skript.registerExpression(ExprQuitReason.class, QuitReason.class, ExpressionType.SIMPLE, "[the] (quit|disconnect) (cause|reason)"); + } + + public ExprQuitReason() { + super(QuitReason.class); + } + + // Allow for 'the quit reason was ...' as that's proper grammar support for this event value. + @Override + public boolean setTime(int time) { + if (time == EventValues.TIME_FUTURE) + return super.setTime(time); + return true; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "quit reason"; + } + +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index e86820ed0c2..02909f887b2 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1932,6 +1932,13 @@ moon phases: waxing_crescent: waxing crescent waxing_gibbous: waxing gibbous +# -- Quit Reasons -- +quit reasons: + disconnected: disconnected, quit + erroneous_state: erroneous, erroneous state + kicked: kicked + timed_out: timed out + # -- Boolean -- boolean: true: @@ -2000,6 +2007,7 @@ types: resourcepackstate: resource pack state¦s @a gene: panda gene¦s @a gamerulevalue: gamerule value¦s @a + quitreason: quit reason¦s @a # Skript weathertype: weather type¦s @a From 9d06f0206557c6b47f2dda81ccc0d2a9a34c1f34 Mon Sep 17 00:00:00 2001 From: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:17:49 +0300 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=9A=80=20Add=20`loop-counter`=20&?= =?UTF-8?q?=20Improve=20loops=20(#4595)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ch/njol/skript/effects/EffContinue.java | 48 ++++---- .../java/ch/njol/skript/effects/EffExit.java | 72 ++++++----- .../ch/njol/skript/effects/EffReturn.java | 56 ++++----- .../skript/expressions/ExprLoopIteration.java | 115 ++++++++++++++++++ .../skript/expressions/ExprLoopValue.java | 55 +++++---- .../java/ch/njol/skript/lang/LoopSection.java | 57 +++++++++ .../java/ch/njol/skript/sections/SecLoop.java | 36 +++--- .../ch/njol/skript/sections/SecWhile.java | 32 ++--- .../tests/regressions/4595-loop-iteration.sk | 28 +++++ 9 files changed, 359 insertions(+), 140 deletions(-) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java create mode 100644 src/main/java/ch/njol/skript/lang/LoopSection.java create mode 100644 src/test/skript/tests/regressions/4595-loop-iteration.sk diff --git a/src/main/java/ch/njol/skript/effects/EffContinue.java b/src/main/java/ch/njol/skript/effects/EffContinue.java index 263ffb39059..026ca015d86 100644 --- a/src/main/java/ch/njol/skript/effects/EffContinue.java +++ b/src/main/java/ch/njol/skript/effects/EffContinue.java @@ -25,25 +25,33 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.LoopSection; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; -import ch.njol.skript.lang.TriggerSection; -import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.skript.sections.SecLoop; -import ch.njol.skript.sections.SecWhile; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import java.util.List; -import java.util.stream.Collectors; @Name("Continue") -@Description("Skips the value currently being looped, moving on to the next value if it exists.") -@Examples({"loop all players:", +@Description("Immediately moves the (while) loop on to the next iteration.") +@Examples({ + "# Broadcast online moderators", + "loop all players:", "\tif loop-value does not have permission \"moderator\":", - "\t\tcontinue # filter out non moderators", - "\tbroadcast \"%loop-player% is a moderator!\" # Only moderators get broadcast"}) + "\t\tcontinue # filter out non moderators", + "\tbroadcast \"%loop-player% is a moderator!\" # Only moderators get broadcast", + " ", + "# Game starting counter", + "set {_counter} to 11", + "while {_counter} > 0:", + "\tremove 1 from {_counter}", + "\twait a second", + "\tif {_counter} != 1, 2, 3, 5 or 10:", + "\t\tcontinue # only print when counter is 1, 2, 3, 5 or 10", + "\tbroadcast \"Game starting in %{_counter}% second(s)\"", +}) @Since("2.2-dev37, 2.7 (while loops)") public class EffContinue extends Effect { @@ -52,36 +60,34 @@ public class EffContinue extends Effect { } @SuppressWarnings("NotNullFieldNotInitialized") - private TriggerSection section; + private LoopSection loop; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - List currentSections = ParserInstance.get().getCurrentSections().stream() - .filter(s -> s instanceof SecLoop || s instanceof SecWhile) - .collect(Collectors.toList()); + List currentLoops = getParser().getCurrentSections(LoopSection.class); - if (currentSections.isEmpty()) { - Skript.error("Continue may only be used in while or loops"); + if (currentLoops.isEmpty()) { + Skript.error("The 'continue' effect may only be used in while and regular loops"); return false; } - section = currentSections.get(currentSections.size() - 1); + loop = currentLoops.get(currentLoops.size() - 1); return true; } @Override - protected void execute(Event e) { + protected void execute(Event event) { throw new UnsupportedOperationException(); } - @Nullable @Override - protected TriggerItem walk(Event e) { - return section; + @Nullable + protected TriggerItem walk(Event event) { + return loop; } @Override - public String toString(@Nullable Event e, boolean debug) { + public String toString(@Nullable Event event, boolean debug) { return "continue"; } diff --git a/src/main/java/ch/njol/skript/effects/EffExit.java b/src/main/java/ch/njol/skript/effects/EffExit.java index 64f7fab4cdb..eecbb7efec1 100644 --- a/src/main/java/ch/njol/skript/effects/EffExit.java +++ b/src/main/java/ch/njol/skript/effects/EffExit.java @@ -25,50 +25,51 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.LoopSection; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.TriggerSection; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.log.ErrorQuality; import ch.njol.skript.sections.SecConditional; -import ch.njol.skript.sections.SecLoop; -import ch.njol.skript.sections.SecWhile; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import java.util.List; -/** - * @author Peter Güttinger - */ @Name("Exit") @Description("Exits a given amount of loops and conditionals, or the entire trigger.") -@Examples({"if player has any ore:", - " stop", - "message \"%player% has no ores!\"", - "loop blocks above the player:", - " loop-block is not air:", - " exit 2 sections", - " set loop-block to water"}) +@Examples({ + "if player has any ore:", + "\tstop", + "message \"%player% has no ores!\"", + "loop blocks above the player:", + "\tloop-block is not air:", + "\t\texit 2 sections", + "\tset loop-block to water" +}) @Since("unknown (before 2.1)") public class EffExit extends Effect { // TODO [code style] warn user about code after a stop effect + static { Skript.registerEffect(EffExit.class, "(exit|stop) [trigger]", - "(exit|stop) [(1|a|the|this)] (0¦section|1¦loop|2¦conditional)", - "(exit|stop) <\\d+> (0¦section|1¦loop|2¦conditional)s", - "(exit|stop) all (0¦section|1¦loop|2¦conditional)s"); + "(exit|stop) [(1|a|the|this)] (section|1:loop|2:conditional)", + "(exit|stop) <\\d+> (section|1:loop|2:conditional)s", + "(exit|stop) all (section|1:loop|2:conditional)s"); } private int breakLevels; - private final static int EVERYTHING = 0, LOOPS = 1, CONDITIONALS = 2; - private final static String[] names = {"sections", "loops", "conditionals"}; + private static final int EVERYTHING = 0; + private static final int LOOPS = 1; + private static final int CONDITIONALS = 2; + private static final String[] names = {"sections", "loops", "conditionals"}; private int type; @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) { switch (matchedPattern) { case 0: breakLevels = getParser().getCurrentSections().size() + 1; @@ -102,44 +103,41 @@ private static int numLevels(int type) { List currentSections = ParserInstance.get().getCurrentSections(); if (type == EVERYTHING) return currentSections.size(); - int r = 0; - for (TriggerSection s : currentSections) { - if (type == CONDITIONALS ? s instanceof SecConditional : s instanceof SecLoop || s instanceof SecWhile) - r++; + int level = 0; + for (TriggerSection section : currentSections) { + if (type == CONDITIONALS ? section instanceof SecConditional : section instanceof LoopSection) + level++; } - return r; + return level; } @Override @Nullable - protected TriggerItem walk(final Event e) { - debug(e, false); - TriggerItem n = this; + protected TriggerItem walk(Event event) { + debug(event, false); + TriggerItem node = this; for (int i = breakLevels; i > 0;) { - n = n.getParent(); - if (n == null) { + node = node.getParent(); + if (node == null) { assert false : this; return null; } - if (n instanceof SecLoop) { - ((SecLoop) n).exit(e); - } else if (n instanceof SecWhile) { - ((SecWhile) n).reset(); - } + if (node instanceof LoopSection) + ((LoopSection) node).exit(event); - if (type == EVERYTHING || type == CONDITIONALS && n instanceof SecConditional || type == LOOPS && (n instanceof SecLoop || n instanceof SecWhile)) + if (type == EVERYTHING || type == CONDITIONALS && node instanceof SecConditional || type == LOOPS && (node instanceof LoopSection)) i--; } - return n instanceof SecLoop ? ((SecLoop) n).getActualNext() : n instanceof SecWhile ? ((SecWhile) n).getActualNext() : n.getNext(); + return node instanceof LoopSection ? ((LoopSection) node).getActualNext() : node.getNext(); } @Override - protected void execute(final Event e) { + protected void execute(Event event) { assert false; } @Override - public String toString(final @Nullable Event e, final boolean debug) { + public String toString(@Nullable Event event, boolean debug) { return "stop " + breakLevels + " " + names[type]; } diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 20cf0bdfb0e..2240c509767 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -26,6 +26,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.LoopSection; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.TriggerSection; @@ -34,19 +35,16 @@ import ch.njol.skript.lang.function.ScriptFunction; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; -import ch.njol.skript.sections.SecLoop; -import ch.njol.skript.sections.SecWhile; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; -/** - * @author Peter Güttinger - */ @Name("Return") @Description("Makes a function return a value") -@Examples({"function double(i: number) :: number:", - " return 2 * {_i}"}) +@Examples({ + "function double(i: number) :: number:", + "\treturn 2 * {_i}" +}) @Since("2.2") public class EffReturn extends Effect { @@ -75,18 +73,18 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } function = f; - ClassInfo rt = function.getReturnType(); - if (rt == null) { + ClassInfo returnType = function.getReturnType(); + if (returnType == null) { Skript.error("This function doesn't return any value. Please use 'stop' or 'exit' if you want to stop the function."); return false; } RetainingLogHandler log = SkriptLogger.startRetainingLog(); - Expression v; + Expression convertedExpr; try { - v = exprs[0].getConvertedExpression(rt.getC()); - if (v == null) { - log.printErrors("This function is declared to return " + rt.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); + convertedExpr = exprs[0].getConvertedExpression(returnType.getC()); + if (convertedExpr == null) { + log.printErrors("This function is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); return false; } log.printLog(); @@ -94,33 +92,31 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye log.stop(); } - if (f.isSingle() && !v.isSingle()) { - Skript.error("This function is defined to only return a single " + rt.toString() + ", but this return statement can return multiple values."); + if (f.isSingle() && !convertedExpr.isSingle()) { + Skript.error("This function is defined to only return a single " + returnType.toString() + ", but this return statement can return multiple values."); return false; } - value = v; + value = convertedExpr; return true; } - @SuppressWarnings({"unchecked", "rawtypes"}) @Override @Nullable - protected TriggerItem walk(final Event e) { - debug(e, false); - if (e instanceof FunctionEvent) { - ((ScriptFunction) function).setReturnValue(value.getArray(e)); + @SuppressWarnings({"unchecked", "rawtypes"}) + protected TriggerItem walk(Event event) { + debug(event, false); + if (event instanceof FunctionEvent) { + ((ScriptFunction) function).setReturnValue(value.getArray(event)); } else { - assert false : e; + assert false : event; } TriggerSection parent = getParent(); while (parent != null) { - if (parent instanceof SecLoop) { - ((SecLoop) parent).exit(e); - } else if (parent instanceof SecWhile) { - ((SecWhile) parent).reset(); - } + if (parent instanceof LoopSection) + ((LoopSection) parent).exit(event); + parent = parent.getParent(); } @@ -128,13 +124,13 @@ protected TriggerItem walk(final Event e) { } @Override - protected void execute(Event e) { + protected void execute(Event event) { assert false; } @Override - public String toString(@Nullable Event e, boolean debug) { - return "return " + value.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + return "return " + value.toString(event, debug); } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java b/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java new file mode 100644 index 00000000000..b95471a2cba --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java @@ -0,0 +1,115 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.LoopSection; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.TriggerSection; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Loop Iteration") +@Description("Returns the loop's current iteration count (for both normal and while loops).") +@Examples({ + "while player is online:", + "\tgive player 1 stone", + "\twait 5 ticks", + "\tif loop-counter > 30:", + "\t\tstop loop", + "", + "loop {top-balances::*}:", + "\tif loop-iteration <= 10:", + "\t\tbroadcast \"##%loop-iteration% %loop-index% has $%loop-value%\"", +}) +@Since("INSERT VERSION") +public class ExprLoopIteration extends SimpleExpression { + + static { + Skript.registerExpression(ExprLoopIteration.class, Long.class, ExpressionType.SIMPLE, "[the] loop(-| )(counter|iteration)[-%-*number%]"); + } + + @SuppressWarnings("NotNullFieldNotInitialized") + private LoopSection loop; + + private int loopNumber; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + loopNumber = -1; + if (exprs[0] != null) + loopNumber = ((Literal) exprs[0]).getSingle().intValue(); + + int i = 1; + LoopSection loop = null; + + for (LoopSection l : getParser().getCurrentSections(LoopSection.class)) { + if (i < loopNumber) { + i++; + continue; + } + if (loop != null) { + Skript.error("There are multiple loops. Use loop-iteration-1/2/3/etc. to specify which loop-iteration you want."); + return false; + } + loop = l; + if (i == loopNumber) + break; + } + + if (loop == null) { + Skript.error("The loop iteration expression must be used in a loop"); + return false; + } + + this.loop = loop; + return true; + } + + @Override + protected Long[] get(Event event) { + return new Long[]{loop.getLoopCounter(event)}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return Long.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "loop-iteration" + (loopNumber != -1 ? ("-" + loopNumber) : ""); + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java b/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java index 4ef78dd68c6..5767d6f3352 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java @@ -31,7 +31,6 @@ import ch.njol.skript.lang.Variable; import ch.njol.skript.lang.util.ConvertedExpression; import ch.njol.skript.lang.util.SimpleExpression; -import ch.njol.skript.log.ErrorQuality; import ch.njol.skript.registrations.Classes; import org.skriptlang.skript.lang.converter.Converters; import ch.njol.skript.sections.SecLoop; @@ -47,60 +46,69 @@ /** * Used to access a loop's current value. - *

- * TODO expression to get the current # of execution (e.g. loop-index/number/count/etc (not number though)); - * - * @author Peter Güttinger */ @Name("Loop value") -@Description("The currently looped value.") -@Examples({"# countdown:", - "loop 10 times:", - " message \"%11 - loop-number%\"", - " wait a second", - "# generate a 10x10 floor made of randomly colored wool below the player:", - "loop blocks from the block below the player to the block 10 east of the block below the player:", - " loop blocks from the loop-block to the block 10 north of the loop-block:", - " set loop-block-2 to any wool"}) -@Since("1.0") +@Description("Returns the currently looped value.") +@Examples({ + "# Countdown", + "loop 10 times:", + "\tmessage \"%11 - loop-number%\"", + "\twait a second", + "", + "# Generate a 10x10 floor made of randomly colored wool below the player", + "loop blocks from the block below the player to the block 10 east of the block below the player:", + "\tloop blocks from the loop-block to the block 10 north of the loop-block:", + "\t\tset loop-block-2 to any wool", + "", + "loop {top-balances::*}:", + "\tloop-iteration <= 10", + "\tsend \"##%loop-iteration% %loop-index% has $%loop-value%\"", +}) +@Since("1.0, INSERT VERSION (loop-counter)") public class ExprLoopValue extends SimpleExpression { static { Skript.registerExpression(ExprLoopValue.class, Object.class, ExpressionType.SIMPLE, "[the] loop-<.+>"); } - @SuppressWarnings("null") + @SuppressWarnings("NotNullFieldNotInitialized") private String name; - @SuppressWarnings("null") + @SuppressWarnings("NotNullFieldNotInitialized") private SecLoop loop; // whether this loops a variable boolean isVariableLoop = false; // if this loops a variable and isIndex is true, return the index of the variable instead of the value boolean isIndex = false; - + + private static final Pattern LOOP_PATTERN = Pattern.compile("^(.+)-(\\d+)$"); + @Override public boolean init(Expression[] vars, int matchedPattern, Kleenean isDelayed, ParseResult parser) { name = parser.expr; String s = "" + parser.regexes.get(0).group(); int i = -1; - Matcher m = Pattern.compile("^(.+)-(\\d+)$").matcher(s); + Matcher m = LOOP_PATTERN.matcher(s); if (m.matches()) { s = "" + m.group(1); i = Utils.parseInt("" + m.group(2)); } + + if ("counter".equalsIgnoreCase(s) || "iteration".equalsIgnoreCase(s)) // ExprLoopIteration - in case of classinfo conflicts + return false; + Class c = Classes.getClassFromUserInput(s); int j = 1; SecLoop loop = null; for (SecLoop l : getParser().getCurrentSections(SecLoop.class)) { - if ((c != null && c.isAssignableFrom(l.getLoopedExpression().getReturnType())) || "value".equals(s) || l.getLoopedExpression().isLoopOf(s)) { + if ((c != null && c.isAssignableFrom(l.getLoopedExpression().getReturnType())) || "value".equalsIgnoreCase(s) || l.getLoopedExpression().isLoopOf(s)) { if (j < i) { j++; continue; } if (loop != null) { - Skript.error("There are multiple loops that match loop-" + s + ". Use loop-" + s + "-1/2/3/etc. to specify which loop's value you want.", ErrorQuality.SEMANTIC_ERROR); + Skript.error("There are multiple loops that match loop-" + s + ". Use loop-" + s + "-1/2/3/etc. to specify which loop's value you want."); return false; } loop = l; @@ -109,7 +117,7 @@ public boolean init(Expression[] vars, int matchedPattern, Kleenean isDelayed } } if (loop == null) { - Skript.error("There's no loop that matches 'loop-" + s + "'", ErrorQuality.SEMANTIC_ERROR); + Skript.error("There's no loop that matches 'loop-" + s + "'"); return false; } if (loop.getLoopedExpression() instanceof Variable) { @@ -126,9 +134,9 @@ public boolean isSingle() { return true; } - @SuppressWarnings("unchecked") @Override @Nullable + @SuppressWarnings("unchecked") protected ConvertedExpression getConvertedExpr(Class... to) { if (isVariableLoop && !isIndex) { Class superType = (Class) Utils.getSuperType(to); @@ -165,6 +173,7 @@ protected Object[] get(Event e) { one[0] = current.getValue(); return one; } + Object[] one = (Object[]) Array.newInstance(getReturnType(), 1); one[0] = loop.getCurrent(e); return one; diff --git a/src/main/java/ch/njol/skript/lang/LoopSection.java b/src/main/java/ch/njol/skript/lang/LoopSection.java new file mode 100644 index 00000000000..f733ad2eaaf --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/LoopSection.java @@ -0,0 +1,57 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import org.bukkit.event.Event; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Represents a loop section. + * + * @see ch.njol.skript.sections.SecWhile + * @see ch.njol.skript.sections.SecLoop + */ +public abstract class LoopSection extends Section implements SyntaxElement, Debuggable { + + protected final transient Map currentLoopCounter = new WeakHashMap<>(); + + /** + * @param event The event where the loop is used to return its loop iterations + * @return The loop iteration number + */ + public long getLoopCounter(Event event) { + return currentLoopCounter.getOrDefault(event, 1L); + } + + /** + * @return The next {@link TriggerItem} after the loop + */ + public abstract TriggerItem getActualNext(); + + /** + * Exit the loop, used to reset the loop properties such as iterations counter + * @param event The event where the loop is used to reset its relevant properties + */ + public void exit(Event event) { + currentLoopCounter.remove(event); + } + +} diff --git a/src/main/java/ch/njol/skript/sections/SecLoop.java b/src/main/java/ch/njol/skript/sections/SecLoop.java index 1b3826f81ce..8f0682d2bea 100644 --- a/src/main/java/ch/njol/skript/sections/SecLoop.java +++ b/src/main/java/ch/njol/skript/sections/SecLoop.java @@ -26,7 +26,7 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.Section; +import ch.njol.skript.lang.LoopSection; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.Variable; @@ -80,7 +80,7 @@ "the player and loop-value is the actually coins value such as 200" }) @Since("1.0") -public class SecLoop extends Section { +public class SecLoop extends LoopSection { static { Skript.registerSection(SecLoop.class, "loop %objects%"); @@ -96,6 +96,7 @@ public class SecLoop extends Section { private TriggerItem actualNext; @Override + @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, @@ -116,7 +117,7 @@ public boolean init(Expression[] exprs, } if (expr.isSingle()) { - Skript.error("Can't loop " + expr + " because it's only a single value"); + Skript.error("Can't loop '" + expr + "' because it's only a single value"); return false; } @@ -128,35 +129,36 @@ public boolean init(Expression[] exprs, @Override @Nullable - protected TriggerItem walk(Event e) { - Iterator iter = currentIter.get(e); + protected TriggerItem walk(Event event) { + Iterator iter = currentIter.get(event); if (iter == null) { - iter = expr instanceof Variable ? ((Variable) expr).variablesIterator(e) : expr.iterator(e); + iter = expr instanceof Variable ? ((Variable) expr).variablesIterator(event) : expr.iterator(event); if (iter != null) { if (iter.hasNext()) - currentIter.put(e, iter); + currentIter.put(event, iter); else iter = null; } } if (iter == null || !iter.hasNext()) { - exit(e); - debug(e, false); + exit(event); + debug(event, false); return actualNext; } else { - current.put(e, iter.next()); - return walk(e, true); + current.put(event, iter.next()); + currentLoopCounter.put(event, (currentLoopCounter.getOrDefault(event, 0L)) + 1); + return walk(event, true); } } @Override - public String toString(@Nullable Event e, boolean debug) { - return "loop " + expr.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + return "loop " + expr.toString(event, debug); } @Nullable - public Object getCurrent(Event e) { - return current.get(e); + public Object getCurrent(Event event) { + return current.get(event); } public Expression getLoopedExpression() { @@ -170,12 +172,16 @@ public SecLoop setNext(@Nullable TriggerItem next) { } @Nullable + @Override public TriggerItem getActualNext() { return actualNext; } + @Override public void exit(Event event) { current.remove(event); currentIter.remove(event); + super.exit(event); } + } diff --git a/src/main/java/ch/njol/skript/sections/SecWhile.java b/src/main/java/ch/njol/skript/sections/SecWhile.java index 102d6b620f3..18b7c42634a 100644 --- a/src/main/java/ch/njol/skript/sections/SecWhile.java +++ b/src/main/java/ch/njol/skript/sections/SecWhile.java @@ -26,7 +26,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Condition; import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.Section; +import ch.njol.skript.lang.LoopSection; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.util.Kleenean; @@ -34,6 +34,8 @@ import org.eclipse.jdt.annotation.Nullable; import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; @Name("While Loop") @Description("While Loop sections are loops that will just keep repeating as long as a condition is met.") @@ -54,10 +56,10 @@ "\twait 1 second # without using a delay effect the server will crash", }) @Since("2.0, 2.6 (do while)") -public class SecWhile extends Section { +public class SecWhile extends LoopSection { static { - Skript.registerSection(SecWhile.class, "[(1¦do)] while <.+>"); + Skript.registerSection(SecWhile.class, "[(:do)] while <.+>"); } @SuppressWarnings("NotNullFieldNotInitialized") @@ -81,23 +83,23 @@ public boolean init(Expression[] exprs, condition = Condition.parse(expr, "Can't understand this condition: " + expr); if (condition == null) return false; - doWhile = parseResult.mark == 1; - loadOptionalCode(sectionNode); + doWhile = parseResult.hasTag("do"); + loadOptionalCode(sectionNode); super.setNext(this); - return true; } @Nullable @Override - protected TriggerItem walk(Event e) { - if ((doWhile && !ranDoWhile) || condition.check(e)) { + protected TriggerItem walk(Event event) { + if ((doWhile && !ranDoWhile) || condition.check(event)) { ranDoWhile = true; - return walk(e, true); + currentLoopCounter.put(event, (currentLoopCounter.getOrDefault(event, 0L)) + 1); + return walk(event, true); } else { - reset(); - debug(e, false); + exit(event); + debug(event, false); return actualNext; } } @@ -114,12 +116,14 @@ public TriggerItem getActualNext() { } @Override - public String toString(@Nullable Event e, boolean debug) { - return (doWhile ? "do " : "") + "while " + condition.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + return (doWhile ? "do " : "") + "while " + condition.toString(event, debug); } - public void reset() { + @Override + public void exit(Event event) { ranDoWhile = false; + super.exit(event); } } diff --git a/src/test/skript/tests/regressions/4595-loop-iteration.sk b/src/test/skript/tests/regressions/4595-loop-iteration.sk new file mode 100644 index 00000000000..cca967abf79 --- /dev/null +++ b/src/test/skript/tests/regressions/4595-loop-iteration.sk @@ -0,0 +1,28 @@ +test "loop-iteration": + + # Test without variables + loop 2 times: + add loop-iteration to {_1} + loop 2 times: + add loop-iteration-2 to {_2} + loop 2 times: + add loop-iteration-3 to {_3} + + assert {_1} = 3 with "loop-iteration-1 not equal to 3 (value: %{_1}%)" + assert {_2} = 6 with "loop-iteration-2 not equal to 6 (value: %{_2}%)" + assert {_3} = 12 with "loop-iteration-3 not equal to 12 (value: %{_3}%)" + + delete {_1}, {_2} and {_3} # reset + + # Test with variables + add "a" and "b" to {_a::*} and {_b::*} and {_c::*} + loop {_a::*}: + add loop-iteration to {_1} + loop {_b::*}: + add loop-iteration-2 to {_2} + loop {_c::*}: + add loop-iteration-3 to {_3} + + assert {_1} = 3 with "loop-iteration-1 not equal to 3 (variables) (value: %{_1}%)" + assert {_2} = 6 with "loop-iteration-2 not equal to 6 (variables) (value: %{_2}%)" + assert {_3} = 12 with "loop-iteration-3 not equal to 12 (variables) (value: %{_3}%)" From e08ec477cfbc8a748aa72aac2ee51e0541bb8d4b Mon Sep 17 00:00:00 2001 From: sovdee Date: Sun, 25 Jun 2023 23:21:15 -0700 Subject: [PATCH 07/12] Add CondIsLeftHanded and EffHandedness (#5494) --- .../skript/conditions/CondIsLeftHanded.java | 87 +++++++++++++++++++ .../ch/njol/skript/effects/EffHandedness.java | 77 ++++++++++++++++ .../tests/syntaxes/effects/EffHandedness.sk | 11 +++ 3 files changed, 175 insertions(+) create mode 100644 src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java create mode 100644 src/main/java/ch/njol/skript/effects/EffHandedness.java create mode 100644 src/test/skript/tests/syntaxes/effects/EffHandedness.sk diff --git a/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java b/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java new file mode 100644 index 00000000000..ab2f4b2e26b --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java @@ -0,0 +1,87 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +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.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; +import org.bukkit.inventory.MainHand; + +@Name("Left Handed") +@Description({ + "Checks if living entities or players are left or right-handed. Armor stands are neither right nor left-handed.", + "Paper 1.17.1+ is required for non-player entities." +}) +@Examples({ + "on damage of player:", + "\tif victim is left handed:", + "\t\tcancel event" +}) +@Since("INSERT VERSION") +@RequiredPlugins("Paper 1.17.1+ (entities)") +public class CondIsLeftHanded extends PropertyCondition { + + private static final boolean CAN_USE_ENTITIES = Skript.methodExists(Mob.class, "isLeftHanded"); + + static { + if (CAN_USE_ENTITIES) { + register(CondIsLeftHanded.class, PropertyType.BE, "(:left|right)( |-)handed", "livingentities"); + } else { + register(CondIsLeftHanded.class, PropertyType.BE, "(:left|right)( |-)handed", "players"); + } + } + + private MainHand hand; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + hand = parseResult.hasTag("left") ? MainHand.LEFT : MainHand.RIGHT; + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + public boolean check(LivingEntity livingEntity) { + // check if entity is a mob and if the method exists + if (CAN_USE_ENTITIES && livingEntity instanceof Mob) + return ((Mob) livingEntity).isLeftHanded() == (hand == MainHand.LEFT); + + // check if entity is a player + if (livingEntity instanceof HumanEntity) + return ((HumanEntity) livingEntity).getMainHand() == hand; + + // invalid entity + return false; + } + + @Override + protected String getPropertyName() { + return (hand == MainHand.LEFT ? "left" : "right") + " handed"; + } + +} diff --git a/src/main/java/ch/njol/skript/effects/EffHandedness.java b/src/main/java/ch/njol/skript/effects/EffHandedness.java new file mode 100644 index 00000000000..ddb8a8d0787 --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffHandedness.java @@ -0,0 +1,77 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.effects; + +import ch.njol.skript.Skript; +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.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Handedness") +@Description("Make mobs left or right-handed. This does not affect players.") +@Examples({ + "spawn skeleton at spawn of world \"world\":", + "\tmake entity left handed", + "", + "make all zombies in radius 10 of player right handed" +}) +@Since("INSERT VERSION") +@RequiredPlugins("Paper 1.17.1+") +public class EffHandedness extends Effect { + + static { + if (Skript.methodExists(Mob.class, "setLeftHanded", boolean.class)) + Skript.registerEffect(EffHandedness.class, "make %livingentities% (:left|right)( |-)handed"); + } + + private boolean leftHanded; + private Expression livingEntities; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + leftHanded = parseResult.hasTag("left"); + livingEntities = (Expression) exprs[0]; + return true; + } + + @Override + protected void execute(Event event) { + for (LivingEntity livingEntity : livingEntities.getArray(event)) { + if (livingEntity instanceof Mob) { + ((Mob) livingEntity).setLeftHanded(leftHanded); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "make " + livingEntities.toString(event, debug) + " " + (leftHanded ? "left" : "right") + " handed"; + } + +} diff --git a/src/test/skript/tests/syntaxes/effects/EffHandedness.sk b/src/test/skript/tests/syntaxes/effects/EffHandedness.sk new file mode 100644 index 00000000000..f4df6141be5 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffHandedness.sk @@ -0,0 +1,11 @@ +test "left handedness" when running minecraft "1.17.1": + spawn skeleton at spawn of world "world": + make entity right handed + assert entity is not left handed with "zombie is left handed after being made right handed" + assert entity is right handed with "zombie is not right handed after being made right handed" + make entity left handed + assert entity is not right handed with "zombie is right handed after being made left handed" + assert entity is left handed with "zombie is not left handed after being made left handed" + make entity left handed + assert entity is left handed with "zombie is not left handed after being made left handed again" + delete entity From 59b4bb594be76c179099e56842dcf7499d0868bb Mon Sep 17 00:00:00 2001 From: DelayedGaming <72163224+DelayedGaming@users.noreply.github.com> Date: Tue, 27 Jun 2023 10:08:51 +0800 Subject: [PATCH 08/12] New Event: PlayerPickupArrowEvent (#5588) --- .../classes/data/BukkitEventValues.java | 22 +++++++++++++++++++ .../ch/njol/skript/events/SimpleEvents.java | 11 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 9bf77216c51..2ed28445766 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -109,6 +109,7 @@ import org.bukkit.event.player.PlayerItemDamageEvent; import org.bukkit.event.player.PlayerItemMendEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerPickupArrowEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent.QuitReason; @@ -1649,6 +1650,25 @@ public Location get(LootGenerateEvent event) { }, EventValues.TIME_NOW); } + // PlayerPickupArrowEvent + // This event value is restricted to MC 1.14+ due to an API change which has the return type changed + // which throws a NoSuchMethodError if used in a 1.13 server. + if (Skript.isRunningMinecraft(1, 14)) + EventValues.registerEventValue(PlayerPickupArrowEvent.class, Projectile.class, new Getter() { + @Override + public Projectile get(PlayerPickupArrowEvent event) { + return event.getArrow(); + } + }, EventValues.TIME_NOW); + + EventValues.registerEventValue(PlayerPickupArrowEvent.class, ItemStack.class, new Getter() { + @Override + @Nullable + public ItemStack get(PlayerPickupArrowEvent event) { + return event.getItem().getItemStack(); + } + }, EventValues.TIME_NOW); + //PlayerQuitEvent if (Skript.classExists("org.bukkit.event.player.PlayerQuitEvent$QuitReason")) EventValues.registerEventValue(PlayerQuitEvent.class, QuitReason.class, new Getter() { @@ -1658,5 +1678,7 @@ public QuitReason get(PlayerQuitEvent event) { return event.getReason(); } }, EventValues.TIME_NOW); + } + } diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index bcb301489e1..60753f6969d 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -80,6 +80,7 @@ import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLocaleChangeEvent; import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerPickupArrowEvent; import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; @@ -710,6 +711,16 @@ public class SimpleEvents { .requiredPlugins("Paper 1.16+"); } + Skript.registerEvent("Player Pickup Arrow", SimpleEvent.class, PlayerPickupArrowEvent.class, "[player] (pick[ing| ]up [an] arrow|arrow pick[ing| ]up)") + .description("Called when a player picks up an arrow from the ground.") + .examples( + "on arrow pickup:", + "\tcancel the event", + "\tteleport event-projectile to block 5 above event-projectile" + ) + .since("INSERT VERSION") + .requiredPlugins("Minecraft 1.14+ (event-projectile)"); + Skript.registerEvent("Inventory Drag", SimpleEvent.class, InventoryDragEvent.class, "inventory drag[ging]") .description("Called when a player drags an item in their cursor across the inventory.") .examples( From bb87694b569492dc3233db4dcf56cd0c5e7fc2f4 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Mon, 26 Jun 2023 20:20:47 -0600 Subject: [PATCH 09/12] Add time states for PlayerItemHeldEvent and hotbar slot (#5271) --- .../classes/data/BukkitEventValues.java | 17 +++ .../skript/expressions/ExprHotbarSlot.java | 112 ++++++++++++------ 2 files changed, 96 insertions(+), 33 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 2ed28445766..bc758bb6c6e 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -107,6 +107,7 @@ import org.bukkit.event.player.PlayerItemBreakEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerItemMendEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPickupArrowEvent; @@ -1650,6 +1651,22 @@ public Location get(LootGenerateEvent event) { }, EventValues.TIME_NOW); } + // PlayerItemHeldEvent + EventValues.registerEventValue(PlayerItemHeldEvent.class, Slot.class, new Getter() { + @Override + @Nullable + public Slot get(PlayerItemHeldEvent event) { + return new InventorySlot(event.getPlayer().getInventory(), event.getNewSlot()); + } + }, EventValues.TIME_NOW); + EventValues.registerEventValue(PlayerItemHeldEvent.class, Slot.class, new Getter() { + @Override + @Nullable + public Slot get(PlayerItemHeldEvent event) { + return new InventorySlot(event.getPlayer().getInventory(), event.getPreviousSlot()); + } + }, EventValues.TIME_PAST); + // PlayerPickupArrowEvent // This event value is restricted to MC 1.14+ due to an API change which has the return type changed // which throws a NoSuchMethodError if used in a 1.13 server. diff --git a/src/main/java/ch/njol/skript/expressions/ExprHotbarSlot.java b/src/main/java/ch/njol/skript/expressions/ExprHotbarSlot.java index 0c4e497ff46..ced8c4df34f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHotbarSlot.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHotbarSlot.java @@ -20,70 +20,116 @@ import org.bukkit.entity.Player; import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.inventory.PlayerInventory; import org.eclipse.jdt.annotation.Nullable; -import ch.njol.skript.classes.Changer; +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.Since; -import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.util.Getter; import ch.njol.skript.util.slot.InventorySlot; import ch.njol.skript.util.slot.Slot; +import ch.njol.util.Kleenean; @Name("Hotbar Slot") -@Description({"The currently selected hotbar slot. To retrieve its number use Slot Index expression."}) -@Examples({"message \"%player's current hotbar slot%\"", - "set player's selected hotbar slot to slot 4 of player", - "send \"index of player's current hotbar slot = 1\" # second slot from the left"}) +@Description({ + "The currently selected hotbar slot.", + "To retrieve its number use Slot Index expression.", + "Use future and past tense to grab the previous slot in an item change event, see example." +}) +@Examples({ + "message \"%player's current hotbar slot%\"", + "set player's selected hotbar slot to slot 4 of player", + "", + "send \"index of player's current hotbar slot = 1\" # second slot from the left", + "", + "on item held change:", + "\tif the selected hotbar slot was a diamond:", + "\t\tset the currently selected hotbar slot to slot 5 of player" +}) @Since("2.2-dev36") -public class ExprHotbarSlot extends SimplePropertyExpression { +public class ExprHotbarSlot extends PropertyExpression { static { - register(ExprHotbarSlot.class, Slot.class, "[([currently] selected|current)] hotbar slot", "players"); + registerDefault(ExprHotbarSlot.class, Slot.class, "[the] [([current:currently] selected|current:current)] hotbar slot[s]", "players"); } - - @Override - @Nullable - public Slot convert(Player p) { - PlayerInventory invi = p.getInventory(); - assert invi != null; - return new InventorySlot(invi, invi.getHeldItemSlot()); - } - + + // This exists because time states should not register when the 'currently' tag of the syntax is present. + private boolean current; + @Override - protected String getPropertyName() { - return "hotbar slot"; + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + setExpr((Expression) exprs[0]); + current = parseResult.hasTag("current"); + return true; } - + @Override - public Class getReturnType() { - return Slot.class; + protected Slot[] get(Event event, Player[] source) { + return get(source, new Getter() { + @Override + @Nullable + public Slot get(Player player) { + int time = getTime(); + PlayerInventory inventory = player.getInventory(); + if (event instanceof PlayerItemHeldEvent && time != EventValues.TIME_NOW) { + PlayerItemHeldEvent switchEvent = (PlayerItemHeldEvent) event; + if (time == EventValues.TIME_FUTURE) + return new InventorySlot(inventory, switchEvent.getNewSlot()); + if (time == EventValues.TIME_PAST) + return new InventorySlot(inventory, switchEvent.getPreviousSlot()); + } + return new InventorySlot(inventory, inventory.getHeldItemSlot()); + } + }); } - + @Override @Nullable - public Class[] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.SET) + public Class[] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET) return new Class[] {Slot.class}; return null; } - + @Override - public void change(Event e, @Nullable Object[] delta, Changer.ChangeMode mode) { + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { assert delta != null; Slot slot = (Slot) delta[0]; if (!(slot instanceof InventorySlot)) return; // Only inventory slots can be hotbar slots - + int index = ((InventorySlot) slot).getIndex(); if (index > 8) // Only slots in hotbar can be current hotbar slot return; - - for (Player p : getExpr().getArray(e)) { - p.getInventory().setHeldItemSlot(index); - } + + for (Player player : getExpr().getArray(event)) + player.getInventory().setHeldItemSlot(index); } - + + @Override + public boolean setTime(int time) { + if (current) + return super.setTime(time); + return super.setTime(time, getExpr(), PlayerItemHeldEvent.class); + } + + @Override + public Class getReturnType() { + return Slot.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "hotbar slot of " + getExpr().toString(event, debug); + } + } From f6e4ccf18a1924dd498965e97da6e69ac9def609 Mon Sep 17 00:00:00 2001 From: sovdee Date: Tue, 27 Jun 2023 17:15:19 -0700 Subject: [PATCH 10/12] Vector from direction (#5502) --- .../expressions/ExprVectorFromDirection.java | 94 +++++++++++++++++++ .../java/ch/njol/skript/util/Direction.java | 44 +++++---- 2 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprVectorFromDirection.java diff --git a/src/main/java/ch/njol/skript/expressions/ExprVectorFromDirection.java b/src/main/java/ch/njol/skript/expressions/ExprVectorFromDirection.java new file mode 100644 index 00000000000..38367f92720 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprVectorFromDirection.java @@ -0,0 +1,94 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.util.Direction; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Vectors - Create from Direction") +@Description({ + "Creates vectors from given directions.", + "Relative directions are relative to the origin, (0, 0, 0). Therefore, the vector from the direction 'forwards' is (0, 0, 1)." +}) +@Examples({ + "set {_v} to vector from direction upwards", + "set {_v} to vector in direction of player", + "set {_v} to vector in horizontal direction of player", + "set {_v} to vector from facing of player", + "set {_v::*} to vectors from north, south, east, and west" +}) +@Since("INSERT VERSION") +public class ExprVectorFromDirection extends SimpleExpression { + + static { + Skript.registerExpression(ExprVectorFromDirection.class, Vector.class, ExpressionType.SIMPLE, + "vector[s] [from] %directions%", + "%directions% vector[s]"); + } + + private Expression direction; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + direction = (Expression) exprs[0]; + if (matchedPattern == 1) { + if (!(direction instanceof ExprDirection)) { + Skript.error("The direction in '%directions% vector[s]' can not be a variable. Use the direction expression instead: 'northwards vector'."); + return false; + } + } + return true; + } + + @Override + @Nullable + protected Vector[] get(Event event) { + return direction.stream(event) + .map(Direction::getDirection) + .toArray(Vector[]::new); + } + + @Override + public boolean isSingle() { + return direction.isSingle(); + } + + @Override + public Class getReturnType() { + return Vector.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "vector " + direction.toString(event, debug); + } + +} diff --git a/src/main/java/ch/njol/skript/util/Direction.java b/src/main/java/ch/njol/skript/util/Direction.java index eb8a5f8142f..b50d7d431ef 100644 --- a/src/main/java/ch/njol/skript/util/Direction.java +++ b/src/main/java/ch/njol/skript/util/Direction.java @@ -18,24 +18,6 @@ */ package ch.njol.skript.util; -import java.io.StreamCorruptedException; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Locale; - -import ch.njol.skript.Skript; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Entity; -import org.bukkit.event.Event; -import org.bukkit.material.Directional; -import org.bukkit.util.Vector; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -47,6 +29,20 @@ import ch.njol.util.Kleenean; import ch.njol.yggdrasil.Fields.FieldContext; import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustSerializable; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import java.io.StreamCorruptedException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Locale; /** * @author Peter Güttinger @@ -128,7 +124,17 @@ public Location getRelative(final Entity e) { public Location getRelative(final Block b) { return b.getLocation().add(getDirection(b)); } - + + /* + * Used to get a vector from a direction without anything to be relative to. + * Any relative directions will be relative to 0 degrees pitch and yaw. + */ + public Vector getDirection() { + if (!relative) + return new Vector(pitchOrX, yawOrY, lengthOrZ); + return getDirection(0, 0); + } + public Vector getDirection(final Location l) { if (!relative) return new Vector(pitchOrX, yawOrY, lengthOrZ); From 011c91eb06c0fb8d5bfc0df385ee3d5dc9a0ef9c Mon Sep 17 00:00:00 2001 From: sovdee Date: Tue, 27 Jun 2023 17:21:07 -0700 Subject: [PATCH 11/12] Add event-item and event-slot to Resurrect Event (#5683) --- .../classes/data/BukkitEventValues.java | 17 ++++++++++++++++ .../ch/njol/skript/events/SimpleEvents.java | 20 +++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index bc758bb6c6e..79072ed5454 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -70,6 +70,7 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityEvent; import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.EntityResurrectEvent; import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.FireworkExplodeEvent; import org.bukkit.event.entity.HorseJumpEvent; @@ -132,6 +133,7 @@ import org.bukkit.event.world.PortalCreateEvent; import org.bukkit.event.world.StructureGrowEvent; import org.bukkit.event.world.WorldEvent; +import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; @@ -1651,6 +1653,21 @@ public Location get(LootGenerateEvent event) { }, EventValues.TIME_NOW); } + // EntityResurrectEvent + EventValues.registerEventValue(EntityResurrectEvent.class, Slot.class, new Getter() { + @Override + @Nullable + public Slot get(EntityResurrectEvent event) { + EquipmentSlot hand = event.getHand(); + EntityEquipment equipment = event.getEntity().getEquipment(); + if (equipment == null || hand == null) + return null; + return new ch.njol.skript.util.slot.EquipmentSlot(equipment, + (hand == EquipmentSlot.HAND) ? ch.njol.skript.util.slot.EquipmentSlot.EquipSlot.TOOL + : ch.njol.skript.util.slot.EquipmentSlot.EquipSlot.OFF_HAND); + } + }, EventValues.TIME_NOW); + // PlayerItemHeldEvent EventValues.registerEventValue(PlayerItemHeldEvent.class, Slot.class, new Getter() { @Override diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index 60753f6969d..36a9b2e4fd8 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -472,16 +472,16 @@ public class SimpleEvents { .description("Called when a slime splits. Usually this happens when a big slime dies.") .examples("on slime split:") .since("2.2-dev26"); - if (Skript.classExists("org.bukkit.event.entity.EntityResurrectEvent")) { - Skript.registerEvent("Resurrect Attempt", SimpleEvent.class, EntityResurrectEvent.class, "[entity] resurrect[ion] [attempt]") - .description("Called when an entity dies, always. If they are not holding a totem, this is cancelled - you can, however, uncancel it.") - .examples("on resurrect attempt:", - " entity is player", - " entity has permission \"admin.undying\"", - " uncancel the event") - .since("2.2-dev28"); - SkriptEventHandler.listenCancelled.add(EntityResurrectEvent.class); // Listen this even when cancelled - } + Skript.registerEvent("Resurrect Attempt", SimpleEvent.class, EntityResurrectEvent.class, "[entity] resurrect[ion] [attempt]") + .description("Called when an entity dies, always. If they are not holding a totem, this is cancelled - you can, however, uncancel it.") + .examples( + "on resurrect attempt:", + "\tentity is player", + "\tentity has permission \"admin.undying\"", + "\tuncancel the event" + ) + .since("2.2-dev28"); + SkriptEventHandler.listenCancelled.add(EntityResurrectEvent.class); // Listen this even when cancelled Skript.registerEvent("Player World Change", SimpleEvent.class, PlayerChangedWorldEvent.class, "[player] world chang(ing|e[d])") .description("Called when a player enters a world. Does not work with other entities!") .examples("on player world change:", From 549ebc380b34dd544c760d7a2c23faa8973a12f7 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:18:08 -0600 Subject: [PATCH 12/12] Fix null pointer exception in ExprBlocks when using the ExprDirection without a number defined. (#5790) Update ExprBlocks.java --- src/main/java/ch/njol/skript/expressions/ExprBlocks.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprBlocks.java b/src/main/java/ch/njol/skript/expressions/ExprBlocks.java index 35dea7f01a3..5eccfbb027b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBlocks.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBlocks.java @@ -151,9 +151,11 @@ public Iterator iterator(Event event) { int distance = SkriptConfig.maxTargetBlockDistance.value(); if (this.direction instanceof ExprDirection) { Expression numberExpression = ((ExprDirection) this.direction).amount; - Number number = numberExpression.getSingle(event); - if (numberExpression != null && number != null) - distance = number.intValue(); + if (numberExpression != null) { + Number number = numberExpression.getSingle(event); + if (number != null) + distance = number.intValue(); + } } return new BlockLineIterator(location, vector, distance); } else {