diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java
index e38c523eb62..a85d7a80be4 100644
--- a/src/main/java/ch/njol/skript/Skript.java
+++ b/src/main/java/ch/njol/skript/Skript.java
@@ -95,12 +95,14 @@
import org.skriptlang.skript.bukkit.brewing.BrewingModule;
import org.skriptlang.skript.bukkit.damagesource.DamageSourceModule;
import org.skriptlang.skript.bukkit.displays.DisplayModule;
+import org.skriptlang.skript.bukkit.entity.EntityModule;
import org.skriptlang.skript.bukkit.fishing.FishingModule;
import org.skriptlang.skript.bukkit.furnace.FurnaceModule;
import org.skriptlang.skript.bukkit.input.InputModule;
import org.skriptlang.skript.bukkit.itemcomponents.ItemComponentModule;
import org.skriptlang.skript.bukkit.log.runtime.BukkitRuntimeErrorConsumer;
import org.skriptlang.skript.bukkit.loottables.LootTableModule;
+import org.skriptlang.skript.bukkit.misc.MiscModule;
import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys;
import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;
import org.skriptlang.skript.bukkit.tags.TagModule;
@@ -122,11 +124,13 @@
import org.skriptlang.skript.registration.SyntaxOrigin;
import org.skriptlang.skript.registration.SyntaxRegistry;
import org.skriptlang.skript.util.ClassLoader;
+import org.skriptlang.skript.util.ReflectUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -187,6 +191,7 @@ public final class Skript extends JavaPlugin implements Listener {
private static org.skriptlang.skript.@UnknownNullability Skript skript = null;
private static org.skriptlang.skript.@UnknownNullability Skript unmodifiableSkript = null;
+ private static final ReflectUtils REFLECT_UTILS = new ReflectUtils();
private static boolean disabled = false;
private static boolean partDisabled = false;
@@ -586,7 +591,6 @@ public void onEnable() {
try {
getAddonInstance().loadClasses("ch.njol.skript",
"conditions", "effects", "events", "expressions", "entity", "literals", "sections", "structures");
- getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc");
// todo: become proper module once registry api is merged
FishingModule.load();
BreedingModule.load();
@@ -599,7 +603,9 @@ public void onEnable() {
new DamageSourceModule(),
new ItemComponentModule(),
new BrewingModule(),
- new CommonModule()
+ new CommonModule(),
+ new EntityModule(),
+ new MiscModule()
);
} catch (final Exception e) {
exception(e, "Could not load required .class files: " + e.getLocalizedMessage());
@@ -851,7 +857,7 @@ public void run() {
}
};
}
- }
+ }
private void runTests() {
info("Skript testing environment enabled, starting...");
@@ -1125,80 +1131,6 @@ public static boolean isRunningMinecraft(final Version v) {
return minecraftVersion.compareTo(v) >= 0;
}
- /**
- * Tests whether a given class exists in the classpath.
- *
- * @param className The {@link Class#getCanonicalName() canonical name} of the class
- * @return Whether the given class exists.
- */
- public static boolean classExists(final String className) {
- try {
- Class.forName(className);
- return true;
- } catch (final ClassNotFoundException e) {
- return false;
- }
- }
-
- /**
- * Tests whether a method exists in the given class.
- *
- * @param c The class
- * @param methodName The name of the method
- * @param parameterTypes The parameter types of the method
- * @return Whether the given method exists.
- */
- public static boolean methodExists(final Class> c, final String methodName, final Class>... parameterTypes) {
- try {
- c.getDeclaredMethod(methodName, parameterTypes);
- return true;
- } catch (final NoSuchMethodException e) {
- return false;
- } catch (final SecurityException e) {
- return false;
- }
- }
-
- /**
- * Tests whether a method exists in the given class, and whether the return type matches the expected one.
- *
- * Note that this method doesn't work properly if multiple methods with the same name and parameters exist but have different return types.
- *
- * @param c The class
- * @param methodName The name of the method
- * @param parameterTypes The parameter types of the method
- * @param returnType The expected return type
- * @return Whether the given method exists.
- */
- public static boolean methodExists(final Class> c, final String methodName, final Class>[] parameterTypes, final Class> returnType) {
- try {
- final Method m = c.getDeclaredMethod(methodName, parameterTypes);
- return m.getReturnType() == returnType;
- } catch (final NoSuchMethodException e) {
- return false;
- } catch (final SecurityException e) {
- return false;
- }
- }
-
- /**
- * Tests whether a field exists in the given class.
- *
- * @param c The class
- * @param fieldName The name of the field
- * @return Whether the given field exists.
- */
- public static boolean fieldExists(final Class> c, final String fieldName) {
- try {
- c.getDeclaredField(fieldName);
- return true;
- } catch (final NoSuchFieldException e) {
- return false;
- } catch (final SecurityException e) {
- return false;
- }
- }
-
@Nullable
static Metrics metrics;
@@ -2138,4 +2070,135 @@ public SkriptUpdater getUpdater() {
return updater;
}
+ //
+ /**
+ * Tests whether a given class exists in the classpath.
+ *
+ * @param className The {@link Class#getCanonicalName() canonical name} of the class
+ * @return Whether the given class exists.
+ */
+ public static boolean classExists(String className) {
+ return REFLECT_UTILS.classExists(className);
+ }
+
+ /**
+ * @param className The full package and class name.
+ * @return The resulting {@link Class} if found, otherwise {@code null}.
+ */
+ public static @Nullable Class> getClass(String className) {
+ return REFLECT_UTILS.getClass(className);
+ }
+
+ /**
+ * Tests whether a method exists in the given class.
+ *
+ * @param c The class
+ * @param methodName The name of the method
+ * @param parameterTypes The parameter types of the method
+ * @return Whether the given method exists.
+ */
+ public static boolean methodExists(Class> c, String methodName, Class> @Nullable ... parameterTypes) {
+ return REFLECT_UTILS.methodExists(c, methodName, parameterTypes);
+ }
+
+ /**
+ * Tests whether a method exists in the given class, and whether the return type matches the expected one.
+ *
+ * Note that this method doesn't work properly if multiple methods with the same name and parameters exist but have different return types.
+ *
+ * @param c The class
+ * @param methodName The name of the method
+ * @param parameterTypes The parameter types of the method
+ * @param returnType The expected return type
+ * @return Whether the given method exists.
+ */
+ public static boolean methodExists(Class> c, String methodName, Class> @Nullable [] parameterTypes, Class> returnType) {
+ return REFLECT_UTILS.methodExists(c, methodName, parameterTypes, returnType);
+ }
+
+ /**
+ * @param c The {@link Class} to get the method from.
+ * @param methodName The name of the method.
+ * @param params The {@link Class}es used as parameters for the desired method.
+ * @return The resulting {@link Method} if it exists, otherwise {@code null}.
+ */
+ public static @Nullable Method getMethod(Class> c, String methodName, Class> @Nullable ... params) {
+ return REFLECT_UTILS.getMethod(c, methodName, params);
+ }
+
+ /**
+ * @param c The {@link Class} to get the method from.
+ * @param methodName The name of the method.
+ * @param params The {@link Class}es used as parameters for the desired method.
+ * @param returnType The return type of the desired method.
+ * @return The resulting {@link Method} if it exists, otherwise {@code null}.
+ */
+ public static @Nullable Method getMethod(Class> c, String methodName, Class> @Nullable [] params, @Nullable Class> returnType) {
+ return REFLECT_UTILS.getMethod(c, methodName, params, returnType);
+ }
+
+ /**
+ * Tests whether a field exists in the given class.
+ *
+ * @param c The class
+ * @param fieldName The name of the field
+ * @return Whether the given field exists.
+ */
+ public static boolean fieldExists(Class> c, String fieldName) {
+ return REFLECT_UTILS.fieldExists(c, fieldName);
+ }
+
+ /**
+ * @param c The {@link Class} to get the field from.
+ * @param fieldName The name of the field.
+ * @return The resulting {@link Field} if it exists, otherwise {@code null}.
+ */
+ public static @Nullable Field getField(Class> c, String fieldName) {
+ return REFLECT_UTILS.getField(c, fieldName);
+ }
+
+ /**
+ * Invoke a static {@link Method}.
+ * @param method The {@link Method} to invoke.
+ * @return The result of the invocation if successful, otherwise {@code null}.
+ * @param The expected return type from the invocation.
+ */
+ public static @Nullable Type invokeMethod(Method method) {
+ return REFLECT_UTILS.invokeMethod(method);
+ }
+
+ /**
+ * Invoke a {@link Method}.
+ * @param method The {@link Method} to invoke.
+ * @param holder The holder object to invoke for.
+ * @param params The parameters to pass into the invocation.
+ * @return The result of the invocation if successful, otherwise {@code null}.
+ * @param The expected return type from the invocation.
+ */
+ public static @Nullable Type invokeMethod(Method method, @Nullable Object holder, Object @Nullable ... params) {
+ return REFLECT_UTILS.invokeMethod(method, holder, params);
+ }
+
+ /**
+ * Gets the values of a static {@link Field}.
+ * @param field The {@link Field} to get from.
+ * @return The value of the {@link Field}.
+ * @param The expected return type.
+ */
+ public static @Nullable Type getFieldValue(Field field) {
+ return REFLECT_UTILS.getFieldValue(field);
+ }
+
+ /**
+ * Gets the values of a {@link Field}.
+ * @param field The {@link Field} to get from.
+ * @param holder The holder object to get the field for.
+ * @return The value of the {@link Field}.
+ * @param The expected return type.
+ */
+ public static @Nullable Type getFieldValue(Field field, @Nullable Object holder) {
+ return REFLECT_UTILS.getFieldValue(field, holder);
+ }
+ //
+
}
diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java
index d723321dca0..ed0292a3391 100644
--- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java
+++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java
@@ -241,7 +241,6 @@ private static void addSuperEntity(String codeName, Class extends Entity> enti
addSimpleEntity("happy ghast", HappyGhast.class);
if (Skript.isRunningMinecraft(1, 21, 9)) {
- addSimpleEntity("copper golem", CopperGolem.class);
addSimpleEntity("mannequin", Mannequin.class);
}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
new file mode 100644
index 00000000000..124e5e0dff4
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
@@ -0,0 +1,14 @@
+package org.skriptlang.skript.bukkit.entity;
+
+import org.skriptlang.skript.addon.AddonModule;
+import org.skriptlang.skript.addon.SkriptAddon;
+import org.skriptlang.skript.bukkit.entity.coppergolem.CopperGolemModule;
+
+public class EntityModule implements AddonModule {
+
+ @Override
+ public void load(SkriptAddon addon) {
+ addon.loadModules(new CopperGolemModule());
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemData.java b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemData.java
new file mode 100644
index 00000000000..d14c4599a3c
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemData.java
@@ -0,0 +1,115 @@
+package org.skriptlang.skript.bukkit.entity.coppergolem;
+
+import ch.njol.skript.entity.EntityData;
+import ch.njol.skript.lang.Literal;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.localization.Language;
+import ch.njol.util.Kleenean;
+import io.papermc.paper.world.WeatheringCopperState;
+import org.bukkit.entity.CopperGolem;
+import org.bukkit.entity.CopperGolem.Oxidizing.Waxed;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+public class CopperGolemData extends EntityData {
+
+ public static void register() {
+ register(CopperGolemData.class, "copper golem", CopperGolem.class, 0, "copper golem");
+ }
+
+ private Kleenean waxed = Kleenean.UNKNOWN;
+ private @Nullable WeatheringCopperState state;
+
+ public CopperGolemData() {}
+
+ public CopperGolemData(@Nullable Kleenean waxed, @Nullable WeatheringCopperState state) {
+ this.waxed = waxed == null ? Kleenean.UNKNOWN : waxed;
+ this.state = state;
+ }
+
+ @Override
+ protected boolean init(Literal>[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) {
+ //noinspection unchecked
+ Literal expr = (Literal) exprs[0];
+ if (expr != null)
+ state = expr.getSingle();
+ if (matchedPattern == 1) {
+ waxed = Kleenean.TRUE;
+ } else if (matchedPattern == 2) {
+ waxed = Kleenean.FALSE;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean init(@Nullable Class extends CopperGolem> entityClass, @Nullable CopperGolem golem) {
+ if (golem != null) {
+ state = golem.getWeatheringState();
+ waxed = Kleenean.get(golem.getOxidizing() instanceof Waxed);
+ }
+ return true;
+ }
+
+ @Override
+ public void set(CopperGolem golem) {
+ if (state != null)
+ golem.setWeatheringState(state);
+ if (waxed.isTrue())
+ golem.setOxidizing(CopperGolem.Oxidizing.waxed());
+ }
+
+ @Override
+ protected boolean match(CopperGolem golem) {
+ if (!dataMatch(state, golem.getWeatheringState()))
+ return false;
+ return kleeneanMatch(waxed, Kleenean.get(golem.getOxidizing() instanceof Waxed));
+ }
+
+ @Override
+ public Class extends CopperGolem> getType() {
+ return CopperGolem.class;
+ }
+
+ @Override
+ public @NotNull EntityData> getSuperType() {
+ return new CopperGolemData();
+ }
+
+ @Override
+ protected int hashCode_i() {
+ return Objects.hashCode(state) + waxed.hashCode();
+ }
+
+ @Override
+ protected boolean equals_i(EntityData> entityData) {
+ if (!(entityData instanceof CopperGolemData other))
+ return false;
+ return state == other.state && waxed == other.waxed;
+ }
+
+ @Override
+ public boolean isSupertypeOf(EntityData> entityData) {
+ if (!(entityData instanceof CopperGolemData other))
+ return false;
+ return dataMatch(state, other.state) && kleeneanMatch(waxed, other.waxed);
+ }
+
+ @Override
+ public String toString(int flags) {
+ StringBuilder builder = new StringBuilder();
+ if (waxed.isTrue()) {
+ builder.append("waxed ");
+ } else if (waxed.isFalse()) {
+ builder.append("unwaxed ");
+ }
+ if (state != null)
+ builder.append(Language.getList("weathering copper states." + state.name())[0] + " ");
+ builder.append("copper golem");
+ if (isPlural().isTrue())
+ builder.append("s");
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemModule.java b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemModule.java
new file mode 100644
index 00000000000..da9cff9fa7c
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/CopperGolemModule.java
@@ -0,0 +1,21 @@
+package org.skriptlang.skript.bukkit.entity.coppergolem;
+
+import ch.njol.skript.Skript;
+import org.skriptlang.skript.addon.AddonModule;
+import org.skriptlang.skript.addon.SkriptAddon;
+
+public class CopperGolemModule implements AddonModule {
+
+ @Override
+ public boolean canLoad(SkriptAddon addon) {
+ return Skript.classExists("org.bukkit.entity.CopperGolem");
+ }
+
+ @Override
+ public void load(SkriptAddon addon) {
+ ExprCopperGolemOxidationTime.register(addon.syntaxRegistry());
+
+ CopperGolemData.register();
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/ExprCopperGolemOxidationTime.java b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/ExprCopperGolemOxidationTime.java
new file mode 100644
index 00000000000..afcd865b7d2
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/coppergolem/ExprCopperGolemOxidationTime.java
@@ -0,0 +1,99 @@
+package org.skriptlang.skript.bukkit.entity.coppergolem;
+
+import ch.njol.skript.classes.Changer.ChangeMode;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Example;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.expressions.base.SimplePropertyExpression;
+import ch.njol.skript.util.Timespan;
+import ch.njol.skript.util.Timespan.TimePeriod;
+import ch.njol.util.coll.CollectionUtils;
+import org.bukkit.entity.CopperGolem;
+import org.bukkit.entity.CopperGolem.Oxidizing;
+import org.bukkit.entity.CopperGolem.Oxidizing.AtTime;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+@Name("Time Until Oxidation")
+@Description("""
+ The time until a copper golem oxidizes to its next state. (Normal -> Exposed -> Weathered -> Oxidized).
+ Copper golems that are waxed do not go through oxidation.
+ Setting or resetting the time until oxidation on a waxed copper golem will remove the waxed state.
+ Resetting the time until oxidation uses vanilla behavior of generating a random time between 7 hours and 7 hours 40 minutes.
+ """)
+@Example("set {_time} to the time until oxidation of last spawned copper golem")
+@Example("set the time until oxidation of last spawned copper golem to 10 seconds")
+@Example("clear the time until oxidation of last spawned copper golem")
+@RequiredPlugins("Minecraft 1.21.9+")
+@Since("INSERT VERSION")
+public class ExprCopperGolemOxidationTime extends SimplePropertyExpression {
+
+ public static void register(SyntaxRegistry registry) {
+ registry.register(
+ SyntaxRegistry.EXPRESSION,
+ infoBuilder(
+ ExprCopperGolemOxidationTime.class,
+ Timespan.class,
+ "time until oxidation",
+ "entities",
+ false
+ ).supplier(ExprCopperGolemOxidationTime::new)
+ .build()
+ );
+ }
+
+ @Override
+ public @Nullable Timespan convert(Entity entity) {
+ if (!(entity instanceof CopperGolem golem))
+ return null;
+ if (!(golem.getOxidizing() instanceof AtTime atTime))
+ return null;
+ long worldTime = golem.getWorld().getGameTime();
+ long oxidationTime = atTime.time();
+ if (worldTime > oxidationTime)
+ return null;
+ return new Timespan(TimePeriod.TICK, oxidationTime - worldTime);
+ }
+
+ @Override
+ public Class> @Nullable [] acceptChange(ChangeMode mode) {
+ if (mode == ChangeMode.SET || mode == ChangeMode.RESET)
+ return CollectionUtils.array(Timespan.class);
+ return null;
+ }
+
+ @Override
+ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
+ if (mode == ChangeMode.SET) {
+ assert delta != null;
+ long ticks = ((Timespan) delta[0]).getAs(TimePeriod.TICK);
+ for (Entity entity : getExpr().getArray(event)) {
+ if (!(entity instanceof CopperGolem golem))
+ continue;
+ long worldTime = golem.getWorld().getGameTime();
+ golem.setOxidizing(Oxidizing.atTime(worldTime + ticks));
+ }
+ } else if (mode == ChangeMode.RESET) {
+ for (Entity entity : getExpr().getArray(event)) {
+ if (!(entity instanceof CopperGolem golem))
+ continue;
+ golem.setOxidizing(Oxidizing.unset());
+ }
+ }
+ }
+
+ @Override
+ public Class extends Timespan> getReturnType() {
+ return Timespan.class;
+ }
+
+ @Override
+ protected String getPropertyName() {
+ return "time until oxidation";
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java b/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java
new file mode 100644
index 00000000000..0e340de063f
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java
@@ -0,0 +1,69 @@
+package org.skriptlang.skript.bukkit.misc;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.classes.EnumClassInfo;
+import ch.njol.skript.registrations.Classes;
+import org.bukkit.block.data.type.CopperGolemStatue;
+import org.skriptlang.skript.addon.AddonModule;
+import org.skriptlang.skript.addon.SkriptAddon;
+import org.skriptlang.skript.bukkit.misc.conditions.CondIsWaxed;
+import org.skriptlang.skript.bukkit.misc.effects.EffRotate;
+import org.skriptlang.skript.bukkit.misc.effects.EffWax;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprCopperGolemPose;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprCopperState;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprItemOfEntity;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprQuaternionAxisAngle;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprRotate;
+import org.skriptlang.skript.bukkit.misc.expressions.ExprTextOf;
+import org.skriptlang.skript.bukkit.paperutil.CopperState;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class MiscModule implements AddonModule {
+
+ @Override
+ public void init(SkriptAddon addon) {
+ //noinspection unchecked,rawtypes
+ Classes.registerClass(new EnumClassInfo<>((Class) CopperState.getStateClass(), "weatheringcopperstate", "weathering copper states")
+ .user("(weathering ?)?copper ?states?")
+ .name("Weathering Copper State")
+ .description("The weathering state of a copper golem or copper block.")
+ .since("INSERT VERSION")
+ );
+
+ if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue$Pose")) {
+ Classes.registerClass(new EnumClassInfo<>(CopperGolemStatue.Pose.class, "coppergolempose", "copper golem poses")
+ .user("copper ?golem ?(statue ?)?poses?")
+ .name("Copper Golem Pose")
+ .description("The pose of a copper golem statue.")
+ .requiredPlugins("Minecraft 1.21.9+")
+ .since("INSERT VERSION")
+ );
+ }
+ }
+
+ @Override
+ public void load(SkriptAddon addon) {
+ Set> elementsToLoad = new HashSet<>(Set.of(
+ CondIsWaxed::register,
+ EffRotate::register,
+ EffWax::register,
+ ExprCopperState::register,
+ ExprItemOfEntity::register,
+ ExprQuaternionAxisAngle::register,
+ ExprRotate::register
+ ));
+
+ if (Skript.classExists("org.bukkit.entity.Display"))
+ elementsToLoad.add(ExprTextOf::register);
+ if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue"))
+ elementsToLoad.add(ExprCopperGolemPose::register);
+
+ SyntaxRegistry registry = addon.syntaxRegistry();
+ elementsToLoad.forEach(consumer -> consumer.accept(registry));
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/conditions/CondIsWaxed.java b/src/main/java/org/skriptlang/skript/bukkit/misc/conditions/CondIsWaxed.java
new file mode 100644
index 00000000000..7f2c12fb8c7
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/conditions/CondIsWaxed.java
@@ -0,0 +1,59 @@
+package org.skriptlang.skript.bukkit.misc.conditions;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.conditions.base.PropertyCondition;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Example;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import com.destroystokyo.paper.MaterialTags;
+import org.bukkit.block.Block;
+import org.bukkit.entity.CopperGolem;
+import org.bukkit.entity.CopperGolem.Oxidizing.Waxed;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+@Name("Is Waxed")
+@Description("Whether a copper golem or copper block is waxed.")
+@Example("""
+ if last spawned copper golem is not waxed:
+ wax last spawned copper golem
+ """)
+@Example("""
+ if {_block} is waxed:
+ unwax {_block}
+ """)
+@RequiredPlugins("Minecraft 1.21.9+ (copper golems)")
+@Since("INSERT VERSION")
+public class CondIsWaxed extends PropertyCondition