diff --git a/core/src/main/java/tc/oc/pgm/action/actions/EnchantItemAction.java b/core/src/main/java/tc/oc/pgm/action/actions/EnchantItemAction.java index b704c1d462..d7669c8425 100644 --- a/core/src/main/java/tc/oc/pgm/action/actions/EnchantItemAction.java +++ b/core/src/main/java/tc/oc/pgm/action/actions/EnchantItemAction.java @@ -32,7 +32,11 @@ public void trigger(MatchPlayer player) { } for (int i = 0; i < inv.getSize(); i++) { ItemStack current = inv.getItem(i); - if (current != null && matcher.matches(current)) enchant(current, level); + if (current != null && matcher.matches(current)) { + enchant(current, level); + // Makes item sync with client instantly + inv.setItem(i, current); + } } } diff --git a/core/src/main/java/tc/oc/pgm/kits/KitParser.java b/core/src/main/java/tc/oc/pgm/kits/KitParser.java index a2e66adeda..68bfc065d9 100644 --- a/core/src/main/java/tc/oc/pgm/kits/KitParser.java +++ b/core/src/main/java/tc/oc/pgm/kits/KitParser.java @@ -459,11 +459,6 @@ else if (stack.getAmount() != 1) boolean ignoreEnchantments = XMLUtils.parseBoolean(Node.fromAttr(parent, "ignore-enchantments"), ignoreMetadata); - if (ignoreMetadata && (!ignoreName || !ignoreEnchantments)) { - throw new InvalidXMLException( - "Cannot ignore metadata but respect name or enchantments", parent); - } - return new ItemMatcher( stack, amount, ignoreDurability, ignoreMetadata, ignoreName, ignoreEnchantments); } diff --git a/core/src/main/java/tc/oc/pgm/regions/RegionParser.java b/core/src/main/java/tc/oc/pgm/regions/RegionParser.java index 4ba93a4913..6153d24110 100644 --- a/core/src/main/java/tc/oc/pgm/regions/RegionParser.java +++ b/core/src/main/java/tc/oc/pgm/regions/RegionParser.java @@ -16,16 +16,19 @@ import tc.oc.pgm.util.XMLParser; import tc.oc.pgm.util.xml.InvalidXMLException; import tc.oc.pgm.util.xml.Node; +import tc.oc.pgm.util.xml.XMLFluentParser; import tc.oc.pgm.util.xml.XMLUtils; public abstract class RegionParser implements XMLParser { protected final Map methodParsers; protected final MapFactory factory; + protected final XMLFluentParser parser; public RegionParser(MapFactory factory) { this.factory = factory; this.methodParsers = MethodParsers.getMethodParsersForClass(getClass()); + this.parser = factory.getParser(); } @Override @@ -291,6 +294,16 @@ public MirroredRegion parseMirror(Element el) throws InvalidXMLException { return new MirroredRegion(this.parseChildren(el), origin, normal); } + @MethodParser("resize") + public ResizedRegion parseResize(Element el) throws InvalidXMLException { + Region child = this.parseChildren(el); + Vector min = parser.vector(el, "min").attr().required(); + Vector max = parser.vector(el, "max").attr().required(); + boolean relative = parser.parseBool(el, "relative").attr().orFalse(); + validate(child, BlockBoundedValidation.INSTANCE, new Node(el)); + return new ResizedRegion(child, min, max, relative); + } + @MethodParser("everywhere") public EverywhereRegion parseEverywhere(Element el) throws InvalidXMLException { return EverywhereRegion.INSTANCE; diff --git a/core/src/main/java/tc/oc/pgm/regions/ResizedRegion.java b/core/src/main/java/tc/oc/pgm/regions/ResizedRegion.java new file mode 100644 index 0000000000..f2d7fb81e4 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/regions/ResizedRegion.java @@ -0,0 +1,69 @@ +package tc.oc.pgm.regions; + +import org.bukkit.util.Vector; +import tc.oc.pgm.api.region.Region; +import tc.oc.pgm.util.math.TransformMatrix; + +public class ResizedRegion extends TransformedRegion { + private final Vector min, max; + private final boolean relative; + private TransformMatrix matrix; + private TransformMatrix inverse; + + public ResizedRegion(Region region, Vector min, Vector max, boolean relative) { + super(region); + this.min = min; + this.max = max; + this.relative = relative; + } + + @Override + protected Vector transform(Vector point) { + if (matrix == null) ensureInitialized(); + return matrix.transform(point); + } + + @Override + protected Vector untransform(Vector point) { + if (inverse == null) ensureInitialized(); + return inverse.transform(point); + } + + @Override + public Bounds getBounds() { + ensureInitialized(); + return super.getBounds(); + } + + private void ensureInitialized() { + if (matrix != null) return; + + var oldBounds = region.getBounds(); + if (oldBounds.isEmpty() || !oldBounds.isBlockFinite()) { + this.bounds = oldBounds; + this.matrix = this.inverse = TransformMatrix.identity(); + return; + } + + var oldSize = oldBounds.getSize(); + + if (relative) { + min.multiply(oldSize); + max.multiply(oldSize); + } + + this.bounds = + new Bounds(oldBounds.getMin().subtract(min), oldBounds.getMax().add(max)); + var newSize = bounds.getSize(); + + this.matrix = TransformMatrix.concat( + TransformMatrix.untranslate(oldBounds.getMin()), + TransformMatrix.scale(newSize.clone().divide(oldSize)), + TransformMatrix.translate(bounds.getMin())); + + this.inverse = TransformMatrix.concat( + TransformMatrix.untranslate(bounds.getMin()), + TransformMatrix.scale(oldSize.clone().divide(newSize)), + TransformMatrix.translate(oldBounds.getMin())); + } +} diff --git a/core/src/main/java/tc/oc/pgm/shops/menu/Payment.java b/core/src/main/java/tc/oc/pgm/shops/menu/Payment.java index be1de3b603..b67d21ed82 100644 --- a/core/src/main/java/tc/oc/pgm/shops/menu/Payment.java +++ b/core/src/main/java/tc/oc/pgm/shops/menu/Payment.java @@ -45,7 +45,7 @@ public boolean hasPayment(PlayerInventory inventory) { public boolean matches(ItemStack item) { return this.item != null - ? Materials.itemsSimilar(item, this.item, true, false) + ? Materials.itemsSimilar(item, this.item, true) : item.getType() == currency; } } diff --git a/core/src/main/java/tc/oc/pgm/util/xml/XMLFluentParser.java b/core/src/main/java/tc/oc/pgm/util/xml/XMLFluentParser.java index 0564fe1795..e0bb0f6dec 100644 --- a/core/src/main/java/tc/oc/pgm/util/xml/XMLFluentParser.java +++ b/core/src/main/java/tc/oc/pgm/util/xml/XMLFluentParser.java @@ -20,6 +20,7 @@ import tc.oc.pgm.util.xml.parsers.Builder; import tc.oc.pgm.util.xml.parsers.FilterBuilder; import tc.oc.pgm.util.xml.parsers.ItemBuilder; +import tc.oc.pgm.util.xml.parsers.NumberBuilder; import tc.oc.pgm.util.xml.parsers.PrimitiveBuilder; import tc.oc.pgm.util.xml.parsers.ReferenceBuilder; import tc.oc.pgm.util.xml.parsers.RegionBuilder; @@ -74,6 +75,18 @@ protected String parse(String text) throws TextException { }; } + public NumberBuilder parseInt(Element el, String... prop) { + return number(Integer.class, el, prop); + } + + public NumberBuilder parseDouble(Element el, String... prop) { + return number(Double.class, el, prop); + } + + public NumberBuilder number(Class cls, Element el, String... prop) { + return new NumberBuilder<>(cls, el, prop); + } + public Builder.Generic vector(Element el, String... prop) { return new Builder.Generic<>(el, prop) { @Override diff --git a/core/src/main/java/tc/oc/pgm/util/xml/parsers/NumberBuilder.java b/core/src/main/java/tc/oc/pgm/util/xml/parsers/NumberBuilder.java new file mode 100644 index 0000000000..55dbc4aa53 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/util/xml/parsers/NumberBuilder.java @@ -0,0 +1,34 @@ +package tc.oc.pgm.util.xml.parsers; + +import org.jdom2.Element; +import org.jetbrains.annotations.Nullable; +import tc.oc.pgm.util.xml.InvalidXMLException; +import tc.oc.pgm.util.xml.Node; +import tc.oc.pgm.util.xml.XMLUtils; + +public class NumberBuilder extends Builder> { + + private final Class type; + private boolean infinity; + + public NumberBuilder(Class type, @Nullable Element el, String... prop) { + super(el, prop); + this.type = type; + } + + /** Allow infinity like oo or -oo */ + public NumberBuilder inf() { + this.infinity = true; + return this; + } + + @Override + protected T parse(Node node) throws InvalidXMLException { + return XMLUtils.parseNumber(node, type, infinity); + } + + @Override + protected NumberBuilder getThis() { + return this; + } +} diff --git a/util/src/main/java/tc/oc/pgm/util/inventory/ItemMatcher.java b/util/src/main/java/tc/oc/pgm/util/inventory/ItemMatcher.java index 2ee0099957..fec4951ce0 100644 --- a/util/src/main/java/tc/oc/pgm/util/inventory/ItemMatcher.java +++ b/util/src/main/java/tc/oc/pgm/util/inventory/ItemMatcher.java @@ -22,9 +22,6 @@ public ItemMatcher( boolean ignoreMetadata, boolean ignoreName, boolean ignoreEnchantments) { - if (ignoreMetadata && (!ignoreName || !ignoreEnchantments)) - throw new UnsupportedOperationException( - "Cannot ignore metadata but respect name or enchantments"); this.ignoreDurability = ignoreDurability; @@ -36,20 +33,31 @@ public ItemMatcher( this.base = stripMeta(base); } - private ItemStack stripMeta(ItemStack item) { - ItemMeta meta = item.getItemMeta(); - if (meta == null || (!ignoreMetadata && !(ignoreEnchantments && meta.hasEnchants()))) - return item; + private ItemStack stripMeta(final ItemStack item) { + // No modification needed + if (!item.hasItemMeta() || (!ignoreMetadata && !ignoreName && !ignoreEnchantments)) return item; - item = item.clone(); - if (ignoreMetadata) item.setItemMeta(null); - else item.getEnchantments().keySet().forEach(item::removeEnchantment); + var newItem = item.clone(); + if (ignoreMetadata) { + // Strip all meta, then re-add if needed + newItem.setItemMeta(null); - return item; + // Restore name or enchants + if (!ignoreName) newItem.getItemMeta().setDisplayName(item.getItemMeta().getDisplayName()); + if (!ignoreEnchantments) newItem.addUnsafeEnchantments(item.getEnchantments()); + } else { + // Strip only specific parts + ItemMeta meta = item.getItemMeta(); + + if (ignoreName) meta.setDisplayName(null); + if (ignoreEnchantments) item.getEnchantments().keySet().forEach(item::removeEnchantment); + } + + return newItem; } public boolean matches(ItemStack query) { - return Materials.itemsSimilar(base, stripMeta(query), ignoreDurability, ignoreName) + return Materials.itemsSimilar(base, stripMeta(query), ignoreDurability) && amount.contains(query.getAmount()); } } diff --git a/util/src/main/java/tc/oc/pgm/util/material/Materials.java b/util/src/main/java/tc/oc/pgm/util/material/Materials.java index 77368d2381..652e43f12f 100644 --- a/util/src/main/java/tc/oc/pgm/util/material/Materials.java +++ b/util/src/main/java/tc/oc/pgm/util/material/Materials.java @@ -94,8 +94,7 @@ static boolean isSolid(Material material) { return material != null && material.isSolid() && !SOLID_EXCLUSIONS.matches(material); } - static boolean itemsSimilar( - ItemStack first, ItemStack second, boolean skipDur, boolean skipCheckingName) { + static boolean itemsSimilar(ItemStack first, ItemStack second, boolean skipDur) { if (first == second) { return true; } @@ -107,36 +106,12 @@ static boolean itemsSimilar( } final boolean hasMeta1 = first.hasItemMeta(); final boolean hasMeta2 = second.hasItemMeta(); - if (!hasMeta1 && !hasMeta2) { - return true; - } + if (!hasMeta1 && !hasMeta2) return true; final ItemMeta meta1 = hasMeta1 ? first.getItemMeta() : null; final ItemMeta meta2 = hasMeta2 ? second.getItemMeta() : null; - final String prevName1 = meta1 != null ? meta1.getDisplayName() : null; - final String prevName2 = meta2 != null ? meta2.getDisplayName() : null; - if (skipCheckingName) { - if (meta1 != null) { - meta1.setDisplayName(null); - } - if (meta2 != null) { - meta2.setDisplayName(null); - } - } - - try { - return Bukkit.getItemFactory().equals(meta1, meta2); - } finally { - if (skipCheckingName) { - if (meta1 != null) { - meta1.setDisplayName(prevName1); - } - if (meta2 != null) { - meta2.setDisplayName(prevName2); - } - } - } + return Bukkit.getItemFactory().equals(meta1, meta2); } static boolean isSolid(MaterialData material) { diff --git a/util/src/main/java/tc/oc/pgm/util/math/TransformMatrix.java b/util/src/main/java/tc/oc/pgm/util/math/TransformMatrix.java new file mode 100644 index 0000000000..17e86be364 --- /dev/null +++ b/util/src/main/java/tc/oc/pgm/util/math/TransformMatrix.java @@ -0,0 +1,97 @@ +package tc.oc.pgm.util.math; + +import org.bukkit.util.Vector; + +public class TransformMatrix { + + private static final TransformMatrix IDENTITY = new TransformMatrix(new double[] { + 1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1 // + }); + private final double[] matrix; + + private TransformMatrix(double[] matrix) { + this.matrix = matrix; + } + + public static TransformMatrix identity() { + return IDENTITY; + } + + public static TransformMatrix translate(Vector vector) { + return new TransformMatrix(new double[] { + 1, 0, 0, vector.getX(), + 0, 1, 0, vector.getY(), + 0, 0, 1, vector.getZ(), + 0, 0, 0, 1 + }); + } + + public static TransformMatrix untranslate(Vector vector) { + return new TransformMatrix(new double[] { + 1, 0, 0, -vector.getX(), + 0, 1, 0, -vector.getY(), + 0, 0, 1, -vector.getZ(), + 0, 0, 0, 1 + }); + } + + public static TransformMatrix scale(Vector vector) { + double x = vector.getX(); + double y = vector.getY(); + double z = vector.getZ(); + return new TransformMatrix(new double[] { + x, 0, 0, 0, // + 0, y, 0, 0, // + 0, 0, z, 0, // + 0, 0, 0, 1 // + }); + } + + public static TransformMatrix concat(TransformMatrix... a) { + if (a.length == 1) return a[0]; + + TransformMatrix result = a[a.length - 1]; + for (int i = a.length - 2; i >= 0; i--) { + result = result.multiply(a[i]); + } + return result; + } + + public TransformMatrix multiply(TransformMatrix other) { + double[] a = this.matrix; + double[] b = other.matrix; + + return new TransformMatrix(new double[] { + a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12], + a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13], + a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14], + a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15], + a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12], + a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13], + a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14], + a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15], + a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12], + a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13], + a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14], + a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15], + a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12], + a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13], + a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14], + a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15], + }); + } + + public Vector transform(Vector point) { + double oldX = point.getX(); + double oldY = point.getY(); + double oldZ = point.getZ(); + + double x = matrix[0] * oldX + matrix[1] * oldY + matrix[2] * oldZ + matrix[3]; + double y = matrix[4] * oldX + matrix[5] * oldY + matrix[6] * oldZ + matrix[7]; + double z = matrix[8] * oldX + matrix[9] * oldY + matrix[10] * oldZ + matrix[11]; + return new Vector(x, y, z); + } +}