diff --git a/gradle.properties b/gradle.properties index 0447d28..10ed26a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ groupid=ch.njol name=skript -version=2.5-beta2 +version=2.5 diff --git a/settings.gradle b/settings.gradle index e394df5..c054a9b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,2 @@ rootProject.name = 'Skript' -include 'skript-worldguard6' -//include 'skript-worldguard7fawe' // Maven repositories are broken +include 'skript-worldguard6' \ No newline at end of file diff --git a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java index 2ee60d1..7bbf70b 100644 --- a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java +++ b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java @@ -150,7 +150,7 @@ public void put(String key, Variation value) { } /** - * Contains all variations. {@link #loadVariedAlias} uses this. + * Contains all variations. */ private final Map variations; @@ -210,7 +210,7 @@ public int applyTags(ItemStack stack, Map tags) { } /** - * Name of an alias used by {@link #addAlias(String, String, Map, Map)} + * Name of an alias used by {@link #addAlias(AliasName, String, Map, Map)} * for registration. */ public static class AliasName { diff --git a/src/main/java/ch/njol/skript/aliases/ItemData.java b/src/main/java/ch/njol/skript/aliases/ItemData.java index 0400770..fd8cad8 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemData.java +++ b/src/main/java/ch/njol/skript/aliases/ItemData.java @@ -36,11 +36,13 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; import org.bukkit.potion.PotionData; import org.eclipse.jdt.annotation.Nullable; @@ -77,6 +79,8 @@ public static class OldItemData { static final MaterialRegistry materialRegistry; + private static final boolean SPAWN_EGG_META_EXISTS = Skript.classExists("org.bukkit.inventory.meta.SpawnEggMeta"); + // Load or create material registry static { Gson gson = new GsonBuilder().serializeNulls().create(); @@ -335,7 +339,7 @@ public MatchQuality matchAlias(ItemData item) { } } - /** + /* * Initially, expect exact match. Lower expectations as new differences * between items are discovered. */ @@ -427,13 +431,23 @@ private static MatchQuality compareItemMetas(ItemMeta first, ItemMeta second) { // Potion data if (second instanceof PotionMeta) { - if (!(first instanceof PotionMeta)) { - return MatchQuality.DIFFERENT; // Second is a potion, first is clearly not - } - // Compare potion type, including extended and level 2 attributes + // Second is a potion, first is clearly not + if (!(first instanceof PotionMeta)) + return MatchQuality.DIFFERENT; return !Objects.equals(first, second) ? MatchQuality.SAME_MATERIAL : quality; } + // Only check spawn egg data on 1.12 and below. See issue #3167 + if (!MaterialRegistry.newMaterials && SPAWN_EGG_META_EXISTS && second instanceof SpawnEggMeta) { + if (!(first instanceof SpawnEggMeta)) { + return MatchQuality.DIFFERENT; // Second is a spawn egg, first is clearly not + } + // Compare spawn egg spawned type + EntityType ourSpawnedType = ((SpawnEggMeta) first).getSpawnedType(); + EntityType theirSpawnedType = ((SpawnEggMeta) second).getSpawnedType(); + return !Objects.equals(ourSpawnedType, theirSpawnedType) ? MatchQuality.SAME_MATERIAL : quality; + } + return quality; } diff --git a/src/main/java/ch/njol/skript/aliases/MaterialRegistry.java b/src/main/java/ch/njol/skript/aliases/MaterialRegistry.java index cb51665..eddc925 100644 --- a/src/main/java/ch/njol/skript/aliases/MaterialRegistry.java +++ b/src/main/java/ch/njol/skript/aliases/MaterialRegistry.java @@ -32,7 +32,7 @@ */ public class MaterialRegistry { - private static final boolean newMaterials = Skript.isRunningMinecraft(1, 13); + static final boolean newMaterials = Skript.isRunningMinecraft(1, 13); /** * Loads a material registry from an array of strings. New materials will diff --git a/src/main/java/ch/njol/skript/conditions/CondIsBlockRedstonePowered.java b/src/main/java/ch/njol/skript/conditions/CondIsBlockRedstonePowered.java index facfda5..0d174ac 100644 --- a/src/main/java/ch/njol/skript/conditions/CondIsBlockRedstonePowered.java +++ b/src/main/java/ch/njol/skript/conditions/CondIsBlockRedstonePowered.java @@ -20,30 +20,60 @@ package ch.njol.skript.conditions; import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +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.Since; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; @Name("Is Block Redstone Powered") -@Description("Checks if a block is powered by redstone") +@Description("Checks if a block is indirectly or directly powered by redstone") @Examples({"if clicked block is redstone powered:", - "\tsend \"This block is well-powered by redstone!\""}) + "\tsend \"This block is well-powered by redstone!\"", + "if clicked block is indirectly redstone powered:", + "\tsend \"This block is indirectly redstone powered.\""}) @Since("2.5") -public class CondIsBlockRedstonePowered extends PropertyCondition { +public class CondIsBlockRedstonePowered extends Condition { static { - register(CondIsBlockRedstonePowered.class, "redstone powered", "blocks"); + Skript.registerCondition(CondIsBlockRedstonePowered.class, + "%blocks% (is|are) redstone powered", + "%blocks% (is|are) indirectly redstone powered", + "%blocks% (is|are)(n't| not) redstone powered", + "%blocks% (is|are)(n't| not) indirectly redstone powered"); + } + + @SuppressWarnings("null") + private Expression blocks; + private boolean isIndirectlyPowered; + + @SuppressWarnings({"unchecked", "null"}) + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + blocks = (Expression) exprs[0]; + isIndirectlyPowered = matchedPattern % 2 == 1; + setNegated(matchedPattern > 1); + return true; } @Override - public boolean check(Block b) { - return b.isBlockPowered(); + public boolean check(Event e) { + return isIndirectlyPowered + ? blocks.check(e, Block::isBlockIndirectlyPowered, isNegated()) + : blocks.check(e, Block::isBlockPowered, isNegated()); } @Override - protected String getPropertyName() { - return "redstone powered"; + public String toString(@Nullable Event e, boolean debug) { + return PropertyCondition.toString(this, PropertyCondition.PropertyType.BE, e, debug, blocks, (isIndirectlyPowered ? "indirectly " : "") + "powered"); } + } diff --git a/src/main/java/ch/njol/skript/conditions/CondIsLoaded.java b/src/main/java/ch/njol/skript/conditions/CondIsLoaded.java index 18feacb..7144443 100644 --- a/src/main/java/ch/njol/skript/conditions/CondIsLoaded.java +++ b/src/main/java/ch/njol/skript/conditions/CondIsLoaded.java @@ -19,38 +19,90 @@ */ package ch.njol.skript.conditions; -import ch.njol.skript.conditions.base.PropertyCondition; +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.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.util.Direction; +import ch.njol.util.Kleenean; import org.bukkit.Bukkit; -import org.bukkit.Chunk; +import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; @Name("Is Loaded") -@Description("Checks whether or not a chunk/world is loaded") -@Examples("if chunk at {home::%player's uuid%} is loaded:") -@Since("2.3") -public class CondIsLoaded extends PropertyCondition { +@Description("Checks whether or not a chunk/world is loaded. 'chunk at 1, 1' uses chunk coords, which are location coords divided by 16.") +@Examples({"if chunk at {home::%player's uuid%} is loaded:", + "if chunk 1, 10 in world \"world\" is loaded:", + "if world(\"lobby\") is loaded:"}) +@Since("2.3, 2.5 (revamp with chunk at location/coords)") +public class CondIsLoaded extends Condition { static { - register(CondIsLoaded.class, "loaded", "worlds/chunks"); + Skript.registerCondition(CondIsLoaded.class, + "chunk[s] %directions% [%locations%] (is|are)[(1¦(n't| not))] loaded", + "chunk [at] %number%, %number% (in|of) [world] %world% is[(1¦(n't| not))] loaded", + "[world[s]] %worlds% (is|are)[(1¦(n't| not))] loaded"); } + @Nullable + private Expression locations; + @Nullable + private Expression x,z; + @Nullable + private Expression world; + private int pattern; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + pattern = matchedPattern; + locations = pattern == 0 ? Direction.combine((Expression) exprs[0], (Expression) exprs[1]) : null; + x = pattern == 1 ? (Expression) exprs[0] : null; + z = pattern == 1 ? (Expression) exprs[1] : null; + world = pattern == 1 ? (Expression) exprs[2] : pattern == 2 ? (Expression) exprs[0] : null; + setNegated(parseResult.mark == 1); + return true; + } + + @SuppressWarnings("null") @Override - public boolean check(Object o) { - if (o instanceof Chunk) - return ((Chunk) o).isLoaded(); - else if (o instanceof World) - return Bukkit.getWorld(((World) o).getName()) != null; + public boolean check(Event e) { + switch (pattern) { + case 0: + return locations.check(e, location -> { + World world = location.getWorld(); + if (world != null) + return world.isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4); + return false; + }, isNegated()); + case 1: + return world.check(e, world -> { + Number x = this.x.getSingle(e); + Number z = this.z.getSingle(e); + if (x == null || z == null) + return false; + return world.isChunkLoaded(x.intValue(), z.intValue()); + }, isNegated()); + case 2: + return world.check(e, world -> Bukkit.getWorld(world.getName()) != null, isNegated()); + } return false; } + @SuppressWarnings("null") @Override - protected String getPropertyName() { - return "loaded"; + public String toString(@Nullable Event e, boolean d) { + String neg = isNegated() ? " not " : " "; + String chunk = pattern == 0 ? "chunk[s] at " + locations.toString(e, d) + (locations.isSingle() ? " is" : " are") + neg + "loaded" : ""; + String chunkC = pattern == 1 ? "chunk (x:" + x.toString(e, d) + ",z:" + z.toString(e, d) + ",w:" + world.toString(e,d) + ") is" + neg + "loaded" : ""; + String world = pattern == 2 ? "world[s] " + this.world.toString(e, d) + (this.world.isSingle() ? " is" : " are") + neg + "loaded" : ""; + return chunk + chunkC + world; } } diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index 09cdd31..324ca63 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -64,6 +64,7 @@ import org.bukkit.entity.Horse; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Husk; +import org.bukkit.entity.Illager; import org.bukkit.entity.Illusioner; import org.bukkit.entity.IronGolem; import org.bukkit.entity.ItemFrame; @@ -81,6 +82,7 @@ import org.bukkit.entity.Phantom; import org.bukkit.entity.PigZombie; import org.bukkit.entity.Piglin; +import org.bukkit.entity.PiglinBrute; import org.bukkit.entity.Pillager; import org.bukkit.entity.PolarBear; import org.bukkit.entity.Projectile; @@ -98,6 +100,7 @@ import org.bukkit.entity.Snowball; import org.bukkit.entity.Snowman; import org.bukkit.entity.SpectralArrow; +import org.bukkit.entity.Spellcaster; import org.bukkit.entity.Spider; import org.bukkit.entity.Squid; import org.bukkit.entity.Stray; @@ -263,6 +266,9 @@ public boolean equals(final @Nullable Object obj) { types.add(new SimpleEntityDataInfo("vindicator", Vindicator.class)); } + if (Skript.classExists("org.bukkit.entity.Illusioner")) // Added in 1.12 + types.add(new SimpleEntityDataInfo("illusioner", Illusioner.class)); + if (Skript.isRunningMinecraft(1, 13)) { // More subtypes, more supertypes - changes needed types.add(new SimpleEntityDataInfo("dolphin", Dolphin.class)); types.add(new SimpleEntityDataInfo("phantom", Phantom.class)); @@ -278,9 +284,7 @@ public boolean equals(final @Nullable Object obj) { if (Skript.isRunningMinecraft(1, 14)) { types.add(new SimpleEntityDataInfo("pillager", Pillager.class)); types.add(new SimpleEntityDataInfo("ravager", Ravager.class)); - types.add(new SimpleEntityDataInfo("wandering trader", WanderingTrader.class)); - types.add(new SimpleEntityDataInfo("raider", Raider.class, true)); } if (Skript.isRunningMinecraft(1, 16)) { @@ -288,11 +292,11 @@ public boolean equals(final @Nullable Object obj) { types.add(new SimpleEntityDataInfo("hoglin", Hoglin.class)); types.add(new SimpleEntityDataInfo("zoglin", Zoglin.class)); types.add(new SimpleEntityDataInfo("strider", Strider.class)); - - } - if (Skript.classExists("org.bukkit.entity.Illusioner")) { - types.add(new SimpleEntityDataInfo("illusioner", Illusioner.class)); } + + if (Skript.classExists("org.bukkit.entity.PiglinBrute")) // Added in 1.16.2 + types.add(new SimpleEntityDataInfo("piglin brute", PiglinBrute.class)); + // Register zombie after Husk and Drowned to make sure both work types.add(new SimpleEntityDataInfo("zombie", Zombie.class)); @@ -315,6 +319,13 @@ public boolean equals(final @Nullable Object obj) { types.add(new SimpleEntityDataInfo("fish" , Fish.class, true)); types.add(new SimpleEntityDataInfo("any fireball", Fireball.class, true)); + + if (Skript.classExists("org.bukkit.entity.Illager")) { // Introduced in Spigot 1.12 + types.add(new SimpleEntityDataInfo("illager", Illager.class, true)); + types.add(new SimpleEntityDataInfo("spellcaster", Spellcaster.class, true)); + } + if (Skript.classExists("org.bukkit.entity.Raider")) // Introduced in Spigot 1.14 + types.add(new SimpleEntityDataInfo("raider", Raider.class, true)); } static { diff --git a/src/main/java/ch/njol/skript/expressions/ExprDurability.java b/src/main/java/ch/njol/skript/expressions/ExprDurability.java index 45e35b6..cdc0e81 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprDurability.java +++ b/src/main/java/ch/njol/skript/expressions/ExprDurability.java @@ -19,13 +19,16 @@ */ package ch.njol.skript.expressions; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.event.Event; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.Nullable; +import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.ItemUtils; import ch.njol.skript.classes.Changer.ChangeMode; -import ch.njol.skript.classes.Changer.ChangerUtils; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; @@ -37,27 +40,36 @@ /** * @author Peter Güttinger */ -@Name("Data Value") -@Description({"The data value of an item.", - "You usually don't need this expression as you can check and set items with aliases easily, " + - "but this expression can e.g. be used to \"add 1 to data of <item>\", e.g. for cycling through all wool colours."}) -@Examples({"add 1 to the data value of the clicked block"}) +@Name("Data/Damage Value") +@Description({"The data/damage value of an item/block. Data values of blocks are only supported on 1.12.2 and below.", + "You usually don't need this expression as you can check and set items with aliases easily, ", + "but this expression can e.g. be used to \"add 1 to data of <item>\", e.g. for cycling through all wool colours."}) +@Examples({"set damage value of player's tool to 10", + "set data value of target block of player to 3", + "add 1 to the data value of the clicked block", + "reset data value of block at player"}) @Since("1.2") -public class ExprDurability extends SimplePropertyExpression { +public class ExprDurability extends SimplePropertyExpression { + + private static final boolean LEGACY_BLOCK = !Skript.isRunningMinecraft(1, 13); static { - register(ExprDurability.class, Short.class, "((data|damage)[s] [value[s]]|durabilit(y|ies))", "itemstacks/slots"); + register(ExprDurability.class, Number.class, "((data|damage)[s] [value[s]]|durabilit(y|ies))", "itemtypes/blocks/slots"); } @Override @Nullable - public Short convert(final Object o) { + public Number convert(final Object o) { if (o instanceof Slot) { final ItemStack i = ((Slot) o).getItem(); - return i == null ? null : i.getDurability(); - } else { - return ((ItemStack) o).getDurability(); + return i == null ? null : ItemUtils.getDamage(i); + } else if (o instanceof ItemType) { + ItemStack item = ((ItemType) o).getRandom(); + return item != null ? ItemUtils.getDamage(item) : null; + } else if (LEGACY_BLOCK && o instanceof Block) { + return ((Block) o).getData(); } + return null; } @Override @@ -66,52 +78,74 @@ public String getPropertyName() { } @Override - public Class getReturnType() { - return Short.class; + public Class getReturnType() { + return Number.class; } @Override @Nullable public Class[] acceptChange(final ChangeMode mode) { - if (mode == ChangeMode.REMOVE_ALL) - return null; - if (Slot.class.isAssignableFrom(getExpr().getReturnType()) || getExpr().isSingle() && ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, ItemStack.class, ItemType.class)) - return CollectionUtils.array(Number.class); + switch (mode) { + case ADD: + case SET: + case RESET: + case REMOVE: + case DELETE: + return CollectionUtils.array(Number.class); + } return null; } + @SuppressWarnings("null") @Override public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) { int a = delta == null ? 0 : ((Number) delta[0]).intValue(); final Object[] os = getExpr().getArray(e); for (final Object o : os) { - final ItemStack i = o instanceof Slot ? ((Slot) o).getItem() : (ItemStack) o; - if (i == null) - continue; + ItemStack itemStack = null; + Block block = null; + + if (o instanceof ItemType) + itemStack = ((ItemType) o).getRandom(); + else if (o instanceof Slot) + itemStack = ((Slot) o).getItem(); + else if (LEGACY_BLOCK) + block = (Block) o; + else + return; + + int changeValue = itemStack != null ? ItemUtils.getDamage(itemStack) : block != null ? block.getData() : 0; + switch (mode) { case REMOVE: a = -a; //$FALL-THROUGH$ case ADD: - i.setDurability((short) (i.getDurability() + a)); + changeValue += a; break; case SET: - i.setDurability((short) a); + changeValue = a; break; case DELETE: case RESET: - a = 0; - i.setDurability((short) 0); + changeValue = 0; break; case REMOVE_ALL: assert false; } - if (o instanceof Slot) - ((Slot) o).setItem(i); - else if (ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, ItemStack.class)) - getExpr().change(e, new ItemStack[] {i}, ChangeMode.SET); - else - getExpr().change(e, new ItemType[] {new ItemType(i)}, ChangeMode.SET); + if (o instanceof ItemType && itemStack != null) { + ItemUtils.setDamage(itemStack,changeValue); + ((ItemType) o).setTo(new ItemType(itemStack)); + } else if (o instanceof Slot) { + ItemUtils.setDamage(itemStack,changeValue); + ((Slot) o).setItem(itemStack); + } else { + BlockState blockState = ((Block) o).getState(); + try { + blockState.setRawData((byte) Math.max(0, changeValue)); + blockState.update(); + } catch (IllegalArgumentException | NullPointerException ignore) {} // Catch when a user sets the amount too high + } } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprInventoryInfo.java b/src/main/java/ch/njol/skript/expressions/ExprInventoryInfo.java index 8a41c5e..a261dcf 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprInventoryInfo.java +++ b/src/main/java/ch/njol/skript/expressions/ExprInventoryInfo.java @@ -22,80 +22,101 @@ import java.util.ArrayList; import java.util.List; -import org.bukkit.block.BlockState; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; 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.Since; -import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.util.BlockInventoryHolder; +import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -@Name("Inventory Holder/Viewers/Rows") -@Description("Gets the rows/size/viewers/holder of an inventory.") +@Name("Inventory Holder/Viewers/Rows/Slots") +@Description("Gets the amount of rows/slots, viewers and holder of an inventory.") @Examples({"event-inventory's amount of rows", "holder of player's top inventory", "{_inventory}'s viewers"}) -@Since("2.2-dev34") -public class ExprInventoryInfo extends PropertyExpression { - - private final static int HOLDER = 1, VIEWERS = 2, ROWS = 3; - private int type; +@Since("2.2-dev34, 2.5 (slots)") +public class ExprInventoryInfo extends SimpleExpression { + + private final static int HOLDER = 1, VIEWERS = 2, ROWS = 3, SLOTS = 4; static { - PropertyExpression.register(ExprInventoryInfo.class, Object.class, "(" + HOLDER + "¦holder[s]|" + VIEWERS + "¦viewers|" + ROWS + "¦[amount of] rows)", "inventories"); + Skript.registerExpression(ExprInventoryInfo.class, Object.class, ExpressionType.PROPERTY, + "(" + HOLDER + "¦holder[s]|" + VIEWERS + "¦viewers|" + ROWS + "¦[amount of] rows|" + SLOTS + "¦[amount of] slots)" + " of %inventories%", + "%inventories%'[s] (" + HOLDER + "¦holder[s]|" + VIEWERS + "¦viewers|" + ROWS + "¦[amount of] rows|" + SLOTS + "¦[amount of] slots)"); } + + @SuppressWarnings("null") + private Expression inventories; + private int type; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - setExpr((Expression) exprs[0]); + inventories = (Expression) exprs[0]; type = parseResult.mark; return true; } @Override - protected Object[] get(Event e, Inventory[] source) { - + protected Object[] get(Event e) { + Inventory[] inventories = this.inventories.getArray(e); + List objects = new ArrayList<>(); switch (type) { case HOLDER: - return get(source, inv -> { - InventoryHolder holder = inv.getHolder(); - if (holder instanceof BlockState) { - return new BlockInventoryHolder((BlockState) holder); - } - return holder; - }); + for (Inventory inventory : inventories) { + InventoryHolder holder = inventory.getHolder(); + if (holder != null) + objects.add(holder); + } + break; case ROWS: - return get(source, inv -> inv.getSize() / 9); + for (Inventory inventory : inventories) { + int size = inventory.getSize(); + if (size < 9) // Hoppers have a size of 5, we don't want to return 0 + objects.add(1); + else + objects.add(size / 9); + } + break; + case SLOTS: + for (Inventory inventory : inventories) { + objects.add(inventory.getSize()); + } + break; case VIEWERS: - List viewers = new ArrayList<>(); - for (Inventory inventory : source) { - viewers.addAll(inventory.getViewers()); + for (Inventory inventory : inventories) { + objects.addAll(inventory.getViewers()); } - return viewers.toArray(new HumanEntity[0]); + break; default: return new Object[0]; } + return objects.toArray(new Object[0]); } - + @Override - public Class getReturnType() { - return type == HOLDER ? InventoryHolder.class : type == ROWS ? Number.class : Player.class; + public boolean isSingle() { + return inventories.isSingle() && type != VIEWERS; } + @Override + public Class getReturnType() { + return type == HOLDER ? InventoryHolder.class : (type == ROWS || type == SLOTS) ? Number.class : Player.class; + } + @Override public String toString(@Nullable Event e, boolean debug) { - return (type == HOLDER ? "holder of " : type == ROWS ? "rows of " : "viewers of ") + getExpr().toString(e, debug); + return (type == HOLDER ? "holder of " : type == ROWS ? "rows of " : type == SLOTS ? "slots of " : "viewers of ") + inventories.toString(e, debug); } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprNamed.java b/src/main/java/ch/njol/skript/expressions/ExprNamed.java index b6b25fd..8c662ff 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprNamed.java +++ b/src/main/java/ch/njol/skript/expressions/ExprNamed.java @@ -22,6 +22,7 @@ import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.eclipse.jdt.annotation.Nullable; @@ -100,7 +101,7 @@ public Object get(Object obj) { @Override public Class getReturnType() { - return ItemType.class; // For some reason, inventories still work too... Weird + return getExpr().getReturnType() == InventoryType.class ? Inventory.class : ItemType.class; } @Override diff --git a/src/main/java/ch/njol/skript/hooks/economy/expressions/ExprBalance.java b/src/main/java/ch/njol/skript/hooks/economy/expressions/ExprBalance.java index c1ca59d..285e70e 100644 --- a/src/main/java/ch/njol/skript/hooks/economy/expressions/ExprBalance.java +++ b/src/main/java/ch/njol/skript/hooks/economy/expressions/ExprBalance.java @@ -41,11 +41,11 @@ @Examples({"message \"You have %player's money%\" # the currency name will be added automatically", "remove 20$ from the player's balance # replace '$' by whatever currency you use", "add 200 to the player's account # or omit the currency alltogether"}) -@Since("2.0") +@Since("2.0, 2.5 (offline player support)") @RequiredPlugins({"Vault", "a permission plugin that supports Vault"}) public class ExprBalance extends SimplePropertyExpression { static { - register(ExprBalance.class, Money.class, "(money|balance|[bank] account)", "players"); + register(ExprBalance.class, Money.class, "(money|balance|[bank] account)", "offlineplayers"); } @SuppressWarnings("deprecation") diff --git a/src/main/java/ch/njol/skript/hooks/regions/WorldGuardHook.java b/src/main/java/ch/njol/skript/hooks/regions/WorldGuardHook.java index 95c1d00..6b73549 100644 --- a/src/main/java/ch/njol/skript/hooks/regions/WorldGuardHook.java +++ b/src/main/java/ch/njol/skript/hooks/regions/WorldGuardHook.java @@ -61,17 +61,7 @@ public WorldGuardHook() throws IOException {} @Override protected boolean init() { - if (Skript.classExists("com.boydti.fawe.FaweAPI") && Skript.isRunningMinecraft(1, 13)) { // Assume FAWE on MC 1.13+ - try { - Class faweHook = Class.forName("ch.njol.skript.module.worldguard7fawe.WorldGuard7FAWEHook", true, getClass().getClassLoader()); - faweHook.getDeclaredConstructor().newInstance(); - return true; - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Skript.error("An error occurred while trying to enable support for FAWE WorldGuard 7. WorldGuard region support has been disabled!"); - } - return false; - } else if (!Skript.classExists("com.sk89q.worldguard.WorldGuard")) { // Assume WorldGuard 6 + if (!Skript.classExists("com.sk89q.worldguard.WorldGuard")) { // Assume WorldGuard 6 try { Class oldHook = Class.forName("ch.njol.skript.module.worldguard6.WorldGuard6Hook", true, getClass().getClassLoader()); oldHook.getDeclaredConstructor().newInstance(); diff --git a/src/main/java/ch/njol/skript/util/BlockStateBlock.java b/src/main/java/ch/njol/skript/util/BlockStateBlock.java index 1e9cdc5..51e927a 100644 --- a/src/main/java/ch/njol/skript/util/BlockStateBlock.java +++ b/src/main/java/ch/njol/skript/util/BlockStateBlock.java @@ -317,6 +317,11 @@ public void run() { } } + @Override + public boolean applyBoneMeal(BlockFace blockFace) { + return state.getBlock().applyBoneMeal(blockFace); + } + @Override public Collection getDrops() { assert false; @@ -435,4 +440,9 @@ public BoundingBox getBoundingBox() { public BlockSoundGroup getSoundGroup() { return state.getBlock().getSoundGroup(); } + + @Override + public String getTranslationKey() { + return state.getBlock().getTranslationKey(); + } } diff --git a/src/main/java/ch/njol/skript/util/DelayedChangeBlock.java b/src/main/java/ch/njol/skript/util/DelayedChangeBlock.java index bb11e3e..3af5ff9 100644 --- a/src/main/java/ch/njol/skript/util/DelayedChangeBlock.java +++ b/src/main/java/ch/njol/skript/util/DelayedChangeBlock.java @@ -317,6 +317,11 @@ public void run() { } } + @Override + public boolean applyBoneMeal(BlockFace blockFace) { + return b.applyBoneMeal(blockFace); + } + @Override public Collection getDrops() { return b.getDrops(); @@ -399,4 +404,9 @@ public BoundingBox getBoundingBox() { public BlockSoundGroup getSoundGroup() { return b.getSoundGroup(); } + + @Override + public String getTranslationKey() { + return b.getTranslationKey(); + } } diff --git a/src/main/java/ch/njol/skript/util/VisualEffect.java b/src/main/java/ch/njol/skript/util/VisualEffect.java index 5bd278c..a7f9a8a 100644 --- a/src/main/java/ch/njol/skript/util/VisualEffect.java +++ b/src/main/java/ch/njol/skript/util/VisualEffect.java @@ -28,6 +28,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; +import org.bukkit.Particle.DustOptions; import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -61,6 +62,7 @@ public final class VisualEffect implements SyntaxElement, YggdrasilSerializable private final static String LANGUAGE_NODE = "visual effects"; static final boolean newEffectData = Skript.classExists("org.bukkit.block.data.BlockData"); + private static final boolean HAS_REDSTONE_DATA = Skript.classExists("org.bukkit.Particle$DustOptions"); public static enum Type implements YggdrasilSerializable { ENDER_SIGNAL(Effect.ENDER_SIGNAL), @@ -147,6 +149,16 @@ public boolean isColorable() { public boolean isColorable() { return true; } + + @Nullable + @Override + public Object getData(@Nullable Object raw, Location l) { + if (Particle.REDSTONE.getDataType() == DustOptions.class && raw instanceof ParticleOption) { + ParticleOption option = (ParticleOption) raw; + return new DustOptions(option.color.asBukkitColor(), option.size); + } + return null; + } }, SNOWBALL_BREAK(Particle.SNOWBALL), WATER_DRIP(Particle.DRIP_WATER), @@ -224,6 +236,33 @@ else if (raw instanceof ItemType) { } }, + // 1.10 particles + FALLING_DUST("FALLING_DUST") { + @Override + public Object getData(final @Nullable Object raw, final Location l) { + if (raw == null) + return Material.STONE.getData(); + else if (raw instanceof ItemType) { + if (newEffectData) { + ItemStack rand = ((ItemType) raw).getRandom(); + if (rand == null) + return Bukkit.createBlockData(Material.STONE); + return Bukkit.createBlockData(rand.getType()); + } else { + ItemStack rand = ((ItemType) raw).getRandom(); + if (rand == null) + return Material.STONE.getData(); + @SuppressWarnings("deprecation") + MaterialData type = rand.getData(); + assert type != null; + return type; + } + } else { + return raw; + } + } + }, + // 1.11 particles TOTEM("TOTEM"), SPIT("SPIT"), @@ -362,13 +401,9 @@ public void onLanguageChange() { Skript.warning("Missing pattern at '" + (node + ".pattern") + "' in the " + Language.getName() + " language file"); } else { types.add(ts[i]); - if (ts[i].isColorable()) - patterns.add(pattern); - else { - String dVarExpr = Language.get_(LANGUAGE_NODE + ".area_expression"); - if (dVarExpr == null) dVarExpr = ""; - patterns.add(pattern + " " + dVarExpr); - } + String dVarExpr = Language.get_(LANGUAGE_NODE + ".area_expression"); + if (dVarExpr == null) dVarExpr = ""; + patterns.add(pattern + " " + dVarExpr); } if (names[i] == null) names[i] = new Noun(node + ".name"); @@ -411,54 +446,55 @@ public boolean init(final Expression[] exprs, final int matchedPattern, final return false; } + int dPos = 0; // Data index if (type.isColorable()) { - for (Expression expr : exprs) { - if (expr == null) continue; - else if (expr.getReturnType().isAssignableFrom(Color.class)) { - org.bukkit.Color color = ((Color) expr.getSingle(null)).asBukkitColor(); - - /* - * Colored particles use dX, dY and dZ as RGB values which - * have range from 0 to 1. - * - * For now, only speed exactly 1 is allowed. - */ - dX = color.getRed() / 255.0f + 0.00001f; - dY = color.getGreen() / 255.0f; - dZ = color.getBlue() / 255.0f; - speed = 1; - } else { - Skript.exception("Color not specified for colored particle"); + if (HAS_REDSTONE_DATA) { + Color color = SkriptColor.LIGHT_RED; + if (exprs[0] != null) { + color = (Color) exprs[0].getSingle(null); + dPos++; + } + data = new ParticleOption(color, 1); + } else { + for (Expression expr : exprs) { + if (expr == null) continue; + else if (expr.getReturnType().isAssignableFrom(SkriptColor.class)) { + data = expr.getSingle(null); + } else { + Skript.exception("Color not specified for colored particle"); + + } } } } else { - int numberParams = 0; - for (Expression expr : exprs) { - if (expr.getReturnType() == Long.class || expr.getReturnType() == Integer.class || expr.getReturnType() == Number.class) - numberParams++; - } - - int dPos = 0; // Data index Expression expr = exprs[0]; if (expr.getReturnType() != Long.class && expr.getReturnType() != Integer.class && expr.getReturnType() != Number.class) { dPos = 1; data = exprs[0].getSingle(null); } - - if (numberParams == 1) // Only speed - speed = ((Number) exprs[dPos].getSingle(null)).floatValue(); - else if (numberParams == 3) { // Only dX, dY, dZ - dX = ((Number) exprs[dPos].getSingle(null)).floatValue(); - dY = ((Number) exprs[dPos + 1].getSingle(null)).floatValue(); - dZ = ((Number) exprs[dPos + 2].getSingle(null)).floatValue(); - } else if (numberParams == 4){ // Both present - dX = ((Number) exprs[dPos].getSingle(null)).floatValue(); - dY = ((Number) exprs[dPos + 1].getSingle(null)).floatValue(); - dZ = ((Number) exprs[dPos + 2].getSingle(null)).floatValue(); - speed = ((Number) exprs[dPos + 3].getSingle(null)).floatValue(); + } + + int numberParams = 0; + if (exprs.length > dPos) { + for (int i = dPos; i < exprs.length; i++) { + if (exprs[i].getReturnType() == Long.class || exprs[i].getReturnType() == Integer.class || exprs[i].getReturnType() == Number.class) + numberParams++; } } + if (numberParams == 1) // Only speed + speed = ((Number) exprs[dPos].getSingle(null)).floatValue(); + else if (numberParams == 3) { // Only dX, dY, dZ + dX = ((Number) exprs[dPos].getSingle(null)).floatValue(); + dY = ((Number) exprs[dPos + 1].getSingle(null)).floatValue(); + dZ = ((Number) exprs[dPos + 2].getSingle(null)).floatValue(); + } else if (numberParams == 4){ // Both present + dX = ((Number) exprs[dPos].getSingle(null)).floatValue(); + dY = ((Number) exprs[dPos + 1].getSingle(null)).floatValue(); + dZ = ((Number) exprs[dPos + 2].getSingle(null)).floatValue(); + speed = ((Number) exprs[dPos + 3].getSingle(null)).floatValue(); + } + return true; } @@ -516,8 +552,9 @@ public void play(final @Nullable Player[] ps, final Location l, final @Nullable if (ps == null) { // Colored particles must be played one at time; otherwise, colors are broken if (type.isColorable()) { - for (int i = 0; i < count; i++) { - l.getWorld().spawnParticle(particle, l, 0, dX, dY, dZ, speed, pData); + int c = count == 0 ? 1 : count; + for (int i = 0; i < c; i++) { + l.getWorld().spawnParticle(particle, l, 1, dX, dY, dZ, speed, pData); } } else { l.getWorld().spawnParticle(particle, l, count, dX, dY, dZ, speed, pData); @@ -525,8 +562,9 @@ public void play(final @Nullable Player[] ps, final Location l, final @Nullable } else { for (final Player p : ps) { if (type.isColorable()) { - for (int i = 0; i < count; i++) { - p.spawnParticle(particle, l, 0, dX, dY, dZ, speed, pData); + int c = count == 0 ? 1 : count; + for (int i = 0; i < c; i++) { + p.spawnParticle(particle, l, 1, dX, dY, dZ, speed, pData); } } else { p.spawnParticle(particle, l, count, dX, dY, dZ, speed, pData); @@ -609,4 +647,20 @@ public boolean equals(final @Nullable Object obj) { return true; } + static class ParticleOption { + + Color color; + float size; + + public ParticleOption(Color color, float size) { + this.color = color; + this.size = size; + } + + @Override + public String toString() { + return "ParticleOption{" + "color=" + color + ", size=" + size + '}'; + } + } + } diff --git a/src/main/java/ch/njol/skript/util/slot/InventorySlot.java b/src/main/java/ch/njol/skript/util/slot/InventorySlot.java index 1e25102..db322ce 100644 --- a/src/main/java/ch/njol/skript/util/slot/InventorySlot.java +++ b/src/main/java/ch/njol/skript/util/slot/InventorySlot.java @@ -91,12 +91,12 @@ public void setAmount(int amount) { @Override public String toString(@Nullable Event e, boolean debug) { - InventoryHolder holder = invi.getHolder(); + InventoryHolder holder = invi != null ? invi.getHolder() : null; if (holder instanceof BlockState) holder = new BlockInventoryHolder((BlockState) holder); - if (invi.getHolder() != null) { + if (holder != null) { if (invi instanceof CraftingInventory) // 4x4 crafting grid is contained in player too! return "crafting slot " + index + " of " + Classes.toString(holder); diff --git a/src/main/resources/lang/english.lang b/src/main/resources/lang/english.lang index 67e2900..662e50c 100644 --- a/src/main/resources/lang/english.lang +++ b/src/main/resources/lang/english.lang @@ -1087,6 +1087,12 @@ entities: vindicator: name: vindicator¦s pattern: vindicator(|1¦s) + illager: + name: illager¦s + pattern: illager(|1¦s) + spellcaster: + name: spellcaster¦s + pattern: spellcaster(|1¦s) parrot: name: parrot¦s pattern: parrot(|1¦s) @@ -1207,6 +1213,10 @@ entities: strider: name: strider¦s pattern: strider(|1¦s)|(4¦)strider (kid(|1¦s)|child(|1¦ren)) + #1.16.2 entity + piglin brute: + name: piglin brute¦s + pattern: piglin brute(|1¦s)|(4¦)piglin brute (kid(|1¦s)|child(|1¦ren)) # -- Heal Reasons -- heal reasons: @@ -1481,6 +1491,9 @@ visual effects: block_dust: name: block dust @- pattern: [sprinting] dust of [%itemtype%] + falling_dust: + name: falling dust @- + pattern: falling dust of [%itemtype%] totem: name: totem @- pattern: totem diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fc6062c..29a0e1a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -58,7 +58,7 @@ commands: permission: skript.admin usage: /skript help -soft-depend: [SQLibrary, Vault, WorldGuard, Residence, PreciousStones] +softdepend: [SQLibrary, Vault, WorldGuard, Residence, PreciousStones] permissions: skript.*: diff --git a/src/test/skript/tests/regressions/3303-item durability.sk b/src/test/skript/tests/regressions/3303-item durability.sk new file mode 100644 index 0000000..0474823 --- /dev/null +++ b/src/test/skript/tests/regressions/3303-item durability.sk @@ -0,0 +1,15 @@ +test "item durability": + set {_i} to a diamond sword + set durability of {_i} to 500 + assert durability of {_i} = 500 with "Durability of item should have been 500" + +# This SHOULD be 'when minecraft version is not "1.13"' but there seems to be a bug with versions in tests +# This technically should test 1.12.2 and above, but for some reason its not +# Luckily we only want to test this on 1.12.2 and below, so this bug is working in our favor +test "block data value" when minecraft version is "1.12.2": + set block at spawn of world "world" to farmland + set {_b} to block above block at spawn of world "world" + set block at {_b} to fully grown wheat plant + assert data value of block at {_b} = 7 with "Data value of block should have been 7" + set data value of block at {_b} to 1 + assert data value of block at {_b} = 1 with "Data value of block should have been 1" diff --git a/src/test/skript/tests/regressions/3305-named inventory-item.sk b/src/test/skript/tests/regressions/3305-named inventory-item.sk new file mode 100644 index 0000000..116a078 --- /dev/null +++ b/src/test/skript/tests/regressions/3305-named inventory-item.sk @@ -0,0 +1,12 @@ +test "named inventory/itemtype": + set {_a} to a diamond sword named "BOB" + assert name of {_a} = "BOB" with "Name of item should have been 'BOB'" + + set {_h} to hopper inventory named "SENOR HOPPY" + assert type of {_h} = hopper inventory with "Inventory type should have been 'hopper inventory'" + + set {a::1} to a diamond sword + set {b::1} to {a::1} named "BOB" with lore "LORE1" and "LORE2" + assert name of {b::1} = "BOB" with "Name of item should have been 'BOB'" + assert 1st line of lore of {b::1} = "LORE1" with "1st line of lore of item should have been 'LORE1'" + assert 2nd line of lore of {b::1} = "LORE2" with "2nd line of lore of item should have been 'LORE2'" diff --git a/src/test/skript/tests/regressions/3314-chunk is loaded.sk b/src/test/skript/tests/regressions/3314-chunk is loaded.sk new file mode 100644 index 0000000..a3b63bd --- /dev/null +++ b/src/test/skript/tests/regressions/3314-chunk is loaded.sk @@ -0,0 +1,12 @@ +test "chunk/world is loaded": + #Chunks + assert chunk at spawn of world "world" is loaded with "chunk at spawn should have been loaded" + set block at location(16, 1, 16, world "world") to dirt + assert chunk at 1, 1 in world "world" is loaded with "chunk at 1, 1 in world should have been loaded" + assert chunk at location(16, 1, 16, world "world") is loaded with "chunk at location(1, 1, 1, world ""world"") should be loaded" + assert chunk at 1000, 1000 in world "world" is not loaded with "chunk at 1000, 1000 in world should not have been loaded" + assert chunk at location(1000, 1, 1000, world "world") is not loaded with "chunk at location(1000, 1, 1000, world ""world"") should not be loaded" + + #World + assert world("world") is loaded with "world ""world"" should have been loaded" + assert world("fake-world") is not loaded with "world ""fake-world"" should not have been loaded"