diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index e38c523eb62..58155091822 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -95,6 +95,7 @@ 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; @@ -599,7 +600,8 @@ public void onEnable() { new DamageSourceModule(), new ItemComponentModule(), new BrewingModule(), - new CommonModule() + new CommonModule(), + new EntityModule() ); } catch (final Exception e) { exception(e, "Could not load required .class files: " + e.getLocalizedMessage()); diff --git a/src/main/java/ch/njol/skript/bukkitutil/NamespacedUtils.java b/src/main/java/ch/njol/skript/bukkitutil/NamespacedUtils.java index 67e5e1812cb..45b99b37d03 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/NamespacedUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/NamespacedUtils.java @@ -4,6 +4,8 @@ import ch.njol.skript.localization.Message; import ch.njol.skript.util.ValidationResult; import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.log.runtime.RuntimeErrorProducer; /** * Utility class for {@link NamespacedKey} @@ -89,4 +91,23 @@ public static boolean isValid(String string) { return checkValidation(string).valid(); } + /** + * Helper method for converting a {@link String} to {@link NamespacedKey} during runtime, and printing + * errors or warnings. + * @param producer The {@link RuntimeErrorProducer} instance to print an error or warning. + * @param string The {@link String} to convert to {@link NamespacedKey}. + * @return The resulting {@link NamespacedKey} if valid, otherwise {@code null}. + */ + public static @Nullable NamespacedKey getKeyWithErrors(RuntimeErrorProducer producer, String string) { + ValidationResult result = checkValidation(string); + String message = result.message(); + if (!result.valid()) { + producer.error(message); + return null; + } else if (message != null) { + producer.warning(message); + } + return result.data(); + } + } 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 9794bfc2e22..a9e95480242 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -4,7 +4,11 @@ import ch.njol.skript.bukkitutil.BukkitUtils; import ch.njol.skript.bukkitutil.EntityUtils; import ch.njol.skript.bukkitutil.SkriptTeleportFlag; -import ch.njol.skript.classes.*; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.EnumClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.PatternedParser; +import ch.njol.skript.classes.Serializer; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.expressions.ExprDamageCause; import ch.njol.skript.expressions.base.EventValueExpression; @@ -26,7 +30,12 @@ import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentOffer; -import org.bukkit.entity.*; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Vehicle; +import org.bukkit.entity.Villager; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityPotionEffectEvent; @@ -49,11 +58,18 @@ import org.bukkit.metadata.Metadatable; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.bukkit.profile.PlayerTextures.SkinModel; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.bukkit.base.types.*; +import org.skriptlang.skript.bukkit.base.types.BlockClassInfo; +import org.skriptlang.skript.bukkit.base.types.EntityClassInfo; import org.skriptlang.skript.bukkit.base.types.EntityClassInfo.EntityChanger; +import org.skriptlang.skript.bukkit.base.types.InventoryClassInfo; +import org.skriptlang.skript.bukkit.base.types.ItemStackClassInfo; +import org.skriptlang.skript.bukkit.base.types.NameableClassInfo; +import org.skriptlang.skript.bukkit.base.types.OfflinePlayerClassInfo; +import org.skriptlang.skript.bukkit.base.types.PlayerClassInfo; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; @@ -1140,5 +1156,12 @@ public String toVariableNameString(WorldBorder border) { .since("2.12") ); + Classes.registerClass(new EnumClassInfo<>(SkinModel.class, "skinmodel", "skin models") + .user("skin ?models?") + .name("Skin Model") + .description("Represents a skin model.") + .since("INSERT VERSION") + ); + } } diff --git a/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java b/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java index e7fc5ed1c98..9a67358fb78 100644 --- a/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java +++ b/src/main/java/ch/njol/skript/conditions/CondIsLeftHanded.java @@ -3,38 +3,34 @@ 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.Example; 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.Mannequin; 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." -}) -@Examples({ - "on damage of player:", - "\tif victim is left handed:", - "\t\tcancel event" -}) -@Since("2.8.0") +@Description("Checks if living entities or players are left or right-handed. Armor stands are neither right nor left-handed.") +@Example(""" + on damage of player: + if victim is left handed: + cancel event + """) +@RequiredPlugins("Minecraft 1.21.9+ (mannequins)") +@Since("2.8.0, INSERT VERSION (mannequins)") public class CondIsLeftHanded extends PropertyCondition { - // TODO - remove this when Spigot support is dropped - private static final boolean CAN_USE_ENTITIES = Skript.methodExists(Mob.class, "isLeftHanded"); + private static final boolean MANNEQUIN_EXISTS = Skript.classExists("org.bukkit.entity.Mannequin"); static { - if (CAN_USE_ENTITIES) { - register(CondIsLeftHanded.class, "(:left|right)( |-)handed", "livingentities"); - } else { - register(CondIsLeftHanded.class, "(:left|right)( |-)handed", "players"); - } + register(CondIsLeftHanded.class, "(:left|right)( |-)handed", "entities"); } private MainHand hand; @@ -48,13 +44,16 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, @Override public boolean check(LivingEntity livingEntity) { // check if entity is a mob and if the method exists - if (CAN_USE_ENTITIES && livingEntity instanceof Mob mob) + if (livingEntity instanceof Mob mob) return mob.isLeftHanded() == (hand == MainHand.LEFT); // check if entity is a player if (livingEntity instanceof HumanEntity humanEntity) return humanEntity.getMainHand() == hand; + if (MANNEQUIN_EXISTS && livingEntity instanceof Mannequin mannequin) + return mannequin.getMainHand() == hand; + // invalid entity return false; } diff --git a/src/main/java/ch/njol/skript/effects/EffHandedness.java b/src/main/java/ch/njol/skript/effects/EffHandedness.java index d7f29122d3e..0edc701e3c0 100644 --- a/src/main/java/ch/njol/skript/effects/EffHandedness.java +++ b/src/main/java/ch/njol/skript/effects/EffHandedness.java @@ -2,32 +2,36 @@ import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Examples; +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.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.Mannequin; import org.bukkit.entity.Mob; import org.bukkit.event.Event; +import org.bukkit.inventory.MainHand; import org.jetbrains.annotations.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("2.8.0") +@Example(""" + spawn skeleton at spawn of world "world": + make entity left handed + """) +@Example("make all zombies in radius 10 of player right handed") +@RequiredPlugins("Minecraft 1.21.9+ (mannequins)") +@Since("2.8.0, INSERT VERSION (mannequins)") public class EffHandedness extends Effect { + private static final boolean MANNEQUIN_EXISTS = Skript.classExists("org.bukkit.entity.Mannequin"); + static { - if (Skript.methodExists(Mob.class, "setLeftHanded", boolean.class)) - Skript.registerEffect(EffHandedness.class, "make %livingentities% (:left|right)( |-)handed"); + Skript.registerEffect(EffHandedness.class, "make %livingentities% (:left|right)( |-)handed"); } private boolean leftHanded; @@ -42,9 +46,12 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override protected void execute(Event event) { + MainHand mainHand = leftHanded ? MainHand.LEFT : MainHand.RIGHT; for (LivingEntity livingEntity : livingEntities.getArray(event)) { - if (livingEntity instanceof Mob) { - ((Mob) livingEntity).setLeftHanded(leftHanded); + if (livingEntity instanceof Mob mob) { + mob.setLeftHanded(leftHanded); + } else if (MANNEQUIN_EXISTS && livingEntity instanceof Mannequin mannequin) { + mannequin.setMainHand(mainHand); } } } diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index d723321dca0..3e47b3ab58a 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -242,7 +242,6 @@ private static void addSuperEntity(String codeName, Class enti if (Skript.isRunningMinecraft(1, 21, 9)) { addSimpleEntity("copper golem", CopperGolem.class); - addSimpleEntity("mannequin", Mannequin.class); } // SuperTypes 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..0332265bd43 --- /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.mannequin.MannequinModule; + +public class EntityModule implements AddonModule { + + @Override + public void load(SkriptAddon addon) { + addon.loadModules(new MannequinModule()); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinData.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinData.java new file mode 100644 index 00000000000..707ea5a0fed --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinData.java @@ -0,0 +1,81 @@ +package org.skriptlang.skript.bukkit.entity.mannequin; + +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Mannequin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MannequinData extends EntityData { + + public static void register() { + register(MannequinData.class, "mannequin", Mannequin.class, 0, "mannequin"); + } + + private Kleenean immovable = Kleenean.UNKNOWN; + + public MannequinData() {} + + public MannequinData(@Nullable Kleenean immovable) { + this.immovable = immovable == null ? Kleenean.UNKNOWN : immovable; + } + + @Override + protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { + if (matchedPattern == 1) { + immovable = Kleenean.TRUE; + } else if (matchedPattern == 2) { + immovable = Kleenean.FALSE; + } + return true; + } + + @Override + protected boolean init(@Nullable Class entityClass, @Nullable Mannequin mannequin) { + if (mannequin != null) + immovable = Kleenean.get(mannequin.isImmovable()); + return true; + } + + @Override + public void set(Mannequin mannequin) { + mannequin.setImmovable(immovable.isTrue()); + } + + @Override + protected boolean match(Mannequin mannequin) { + return kleeneanMatch(immovable, mannequin.isImmovable()); + } + + @Override + public Class getType() { + return Mannequin.class; + } + + @Override + public @NotNull EntityData getSuperType() { + return new MannequinData(); + } + + @Override + protected int hashCode_i() { + return immovable.hashCode(); + } + + @Override + protected boolean equals_i(EntityData entityData) { + if (!(entityData instanceof MannequinData other)) + return false; + return immovable == other.immovable; + } + + @Override + public boolean isSupertypeOf(EntityData entityData) { + if (!(entityData instanceof MannequinData other)) + return false; + return kleeneanMatch(immovable, other.immovable); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinModule.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinModule.java new file mode 100644 index 00000000000..117f406faac --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/MannequinModule.java @@ -0,0 +1,40 @@ +package org.skriptlang.skript.bukkit.entity.mannequin; + +import ch.njol.skript.Skript; +import org.skriptlang.skript.addon.AddonModule; +import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.entity.mannequin.elements.*; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.Set; +import java.util.function.Consumer; + +public class MannequinModule implements AddonModule { + + @Override + public boolean canLoad(SkriptAddon addon) { + return Skript.classExists("org.bukkit.entity.Mannequin"); + } + + @Override + public void load(SkriptAddon addon) { + Set> elementsToLoad = Set.of( + CondMannequinImmovable::register, + CondMannequinParts::register, + EffMannequinImmovable::register, + EffMannequinParts::register, + ExprMannequinBody::register, + ExprMannequinCape::register, + ExprMannequinDesc::register, + ExprMannequinElytra::register, + ExprMannequinModel::register, + ExprMannequinSkin::register + ); + + SyntaxRegistry registry = addon.syntaxRegistry(); + elementsToLoad.forEach(element -> element.accept(registry)); + + MannequinData.register(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/ResolvableProfileBuilder.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/ResolvableProfileBuilder.java new file mode 100644 index 00000000000..a22cbd673ef --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/ResolvableProfileBuilder.java @@ -0,0 +1,102 @@ +package org.skriptlang.skript.bukkit.entity.mannequin; + +import com.destroystokyo.paper.profile.ProfileProperty; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +/** + * Helper class for taking a finalized {@link ResolvableProfile} and turning it into a builder. + */ +@SuppressWarnings("UnstableApiUsage") +public class ResolvableProfileBuilder { + + private Collection properties = new ArrayList<>(); + private @Nullable String name = null; + private SkinPatch skinPatch = SkinPatch.empty(); + private @Nullable UUID uuid = null; + + public ResolvableProfileBuilder(ResolvableProfile profile) { + properties = profile.properties(); + name = profile.name(); + skinPatch = profile.skinPatch(); + uuid = profile.uuid(); + } + + /** + * Add {@link ProfileProperty}s to be used in the final {@link ResolvableProfile}. + * @param properties The {@link ProfileProperty}s to add. + * @return {@code this} + */ + public ResolvableProfileBuilder addProperties(Collection properties) { + this.properties.addAll(properties); + return this; + } + + /** + * Add a {@link ProfileProperty} to be used in the final {@link ResolvableProfile}. + * @param property The {@link ProfileProperty} to add. + * @return {@code this} + */ + public ResolvableProfileBuilder addProperty(ProfileProperty property) { + this.properties.add(property); + return this; + } + + /** + * Sets the {@link ProfileProperty}s to be used in the final {@link ResolvableProfile}. + * @param properties The {@link ProfileProperty}s to set. + * @return {@code this} + */ + public ResolvableProfileBuilder setProperties(Collection properties) { + this.properties = properties; + return this; + } + + /** + * The name to be used in the final {@link ResolvableProfile}. + * @param name The name. + * @return {@code this} + */ + public ResolvableProfileBuilder name(@Nullable String name) { + this.name = name; + return this; + } + + /** + * The {@link SkinPatch} to be used in the final {@link ResolvableProfile}. + * @param skinPatch The {@link SkinPatch}. + * @return {@code this} + */ + public ResolvableProfileBuilder skinPatch(SkinPatch skinPatch) { + this.skinPatch = skinPatch; + return this; + } + + /** + * The {@link UUID} to be used in the final {@link ResolvableProfile}. + * @param uuid The {@link UUID}. + * @return {@code this} + */ + public ResolvableProfileBuilder uuid(@Nullable UUID uuid) { + this.uuid = uuid; + return this; + } + + /** + * @return The finalized {@link ResolvableProfile}. + */ + public ResolvableProfile build() { + return ResolvableProfile.resolvableProfile() + .addProperties(properties) + .name(name) + .skinPatch(skinPatch) + .uuid(uuid) + .build(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/SkinPatchBuilder.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/SkinPatchBuilder.java new file mode 100644 index 00000000000..0a18fceb944 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/SkinPatchBuilder.java @@ -0,0 +1,78 @@ +package org.skriptlang.skript.bukkit.entity.mannequin; + +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import net.kyori.adventure.key.Key; +import org.bukkit.profile.PlayerTextures.SkinModel; +import org.jetbrains.annotations.Nullable; + +/** + * Helper class for taking a finalized {@link SkinPatch} and turning it into a builder. + */ +@SuppressWarnings("UnstableApiUsage") +public class SkinPatchBuilder { + + private @Nullable Key body = null; + private @Nullable Key cape = null; + private @Nullable Key elytra = null; + private @Nullable SkinModel model = null; + + public SkinPatchBuilder(SkinPatch skinPatch) { + body = skinPatch.body(); + cape = skinPatch.cape(); + elytra = skinPatch.elytra(); + model = skinPatch.model(); + } + + /** + * The body texture to be used in the final {@link SkinPatch}. + * @param body The body texture. + * @return {@code this} + */ + public SkinPatchBuilder body(@Nullable Key body) { + this.body = body; + return this; + } + + /** + * The cape texture to be used in the final {@link SkinPatch}. + * @param cape The cape texture. + * @return {@code this} + */ + public SkinPatchBuilder cape(@Nullable Key cape) { + this.cape = cape; + return this; + } + + /** + * The elytra texture to be used in the final {@link SkinPatch}. + * @param elytra The elytra texture. + * @return {@code this} + */ + public SkinPatchBuilder elytra(@Nullable Key elytra) { + this.elytra = elytra; + return this; + } + + /** + * The {@link SkinModel} to be used in the final {@link SkinPatch}. + * @param model The {@link SkinModel}. + * @return {@code this} + */ + public SkinPatchBuilder model(@Nullable SkinModel model) { + this.model = model; + return this; + } + + /** + * @return The finalized {@link SkinPatch}. + */ + public SkinPatch build() { + return SkinPatch.skinPatch() + .body(body) + .cape(cape) + .elytra(elytra) + .model(model) + .build(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinImmovable.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinImmovable.java new file mode 100644 index 00000000000..636179685a1 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinImmovable.java @@ -0,0 +1,59 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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 ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin is Immovable") +@Description("Whether a mannequin is immovable or movable.") +@Example(""" + if last spawned mannequin is movable: + make last spawned mannequin immovable + """) +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class CondMannequinImmovable extends PropertyCondition { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.CONDITION, + infoBuilder( + CondMannequinImmovable.class, + PropertyType.BE, + "[:im]movable", + "entities" + ).supplier(CondMannequinImmovable::new) + .build() + ); + } + + private boolean immovable; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + immovable = parseResult.hasTag("im"); + return super.init(expressions, matchedPattern, isDelayed, parseResult); + } + + @Override + public boolean check(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return false; + return mannequin.isImmovable() == immovable; + } + + @Override + protected String getPropertyName() { + return immovable ? "immovable" : "movable"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinParts.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinParts.java new file mode 100644 index 00000000000..c333bf3f904 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/CondMannequinParts.java @@ -0,0 +1,125 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; +import ch.njol.skript.util.Patterns; +import ch.njol.util.Kleenean; +import com.destroystokyo.paper.SkinParts; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.entity.mannequin.elements.EffMannequinParts.MannequinPart; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.function.Function; + +@Name("Mannequin Skin Part is Enabled") +@Description("Whether a specific skin part of a mannequin is enabled or disabled.") +@Example(""" + if the mannequin cape of {_mannequin} is disabled: + enable the mannequin cape of {_mannequin} + """) +@Example(""" + if all of the mannequin skin parts of last spawned mannequin is revealed: + hide all of the mannequin skin parts for last spawned mannequin + """) +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class CondMannequinParts extends Condition { + + private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ + getPattern(MannequinPart.CAPE), + getPattern(MannequinPart.HAT), + getPattern(MannequinPart.JACKET), + getPattern(MannequinPart.LEFT_PANTS), + getPattern(MannequinPart.LEFT_SLEEVE), + getPattern(MannequinPart.RIGHT_PANTS), + getPattern(MannequinPart.RIGHT_SLEEVE), + getPattern(MannequinPart.PANTS), + getPattern(MannequinPart.SLEEVES), + getPattern(MannequinPart.ALL) + }); + + private static Object[] getPattern(MannequinPart part) { + String pattern; + if (part == MannequinPart.ALL) { + pattern = "all [[of] the] mannequin skin parts of %entities% are ((enabled|showing|revealed)|disable:(disabled|hidden))"; + } else { + pattern = "[the] mannequin " + part.part + " [skin] [part[s]] of %entities% (is|are) ((enabled|showing|revealed)|disable:(disabled|hidden))"; + } + return new Object[] {pattern, part}; + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.CONDITION, + SyntaxInfo.builder(CondMannequinParts.class) + .addPatterns(PATTERNS.getPatterns()) + .supplier(CondMannequinParts::new) + .build() + ); + } + + private Expression entities; + private boolean enable; + private MannequinPart mannequinPart; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + entities = (Expression) exprs[0]; + enable = !parseResult.hasTag("disable"); + mannequinPart = PATTERNS.getInfo(matchedPattern); + return true; + } + + @Override + public boolean check(Event event) { + Function checker = switch (mannequinPart) { + case CAPE -> SkinParts::hasCapeEnabled; + case HAT -> SkinParts::hasHatsEnabled; + case JACKET -> SkinParts::hasJacketEnabled; + case LEFT_PANTS -> SkinParts::hasLeftPantsEnabled; + case LEFT_SLEEVE -> SkinParts::hasLeftSleeveEnabled; + case RIGHT_PANTS -> SkinParts::hasRightPantsEnabled; + case RIGHT_SLEEVE -> SkinParts::hasRightSleeveEnabled; + case PANTS -> skin -> skin.hasRightPantsEnabled() && skin.hasRightPantsEnabled(); + case SLEEVES -> skin -> skin.hasRightSleeveEnabled() && skin.hasLeftSleeveEnabled(); + case ALL -> skin -> skin.hasCapeEnabled() && skin.hasHatsEnabled() && skin.hasJacketEnabled() + && skin.hasLeftPantsEnabled() && skin.hasLeftSleeveEnabled() && skin.hasRightPantsEnabled() + && skin.hasRightSleeveEnabled(); + }; + return entities.check(event, entity -> { + if (!(entity instanceof Mannequin mannequin)) + return false; + return checker.apply(mannequin.getSkinParts()) == enable; + }); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + if (mannequinPart == MannequinPart.ALL) { + builder.append("all of the mannequin skin parts"); + } else { + builder.append("the mannequin", mannequinPart.part, "skin part"); + } + builder.append("of", entities, "are"); + if (enable) { + builder.append("enabled"); + } else { + builder.append("disabled"); + } + return builder.toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinImmovable.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinImmovable.java new file mode 100644 index 00000000000..f0a908b034f --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinImmovable.java @@ -0,0 +1,68 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Make Mannequin Immovable") +@Description("Whether a mannequin should be immovable or movable.") +@Example(""" + if last spawned mannequin is movable: + make last spawned mannequin immovable + """) +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class EffMannequinImmovable extends Effect { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EFFECT, + SyntaxInfo.builder(EffMannequinImmovable.class) + .addPatterns("make %entities% [:im]movable") + .supplier(EffMannequinImmovable::new) + .build() + ); + } + + private Expression entities; + private boolean immovable; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + entities = (Expression) exprs[0]; + immovable = parseResult.hasTag("im"); + return true; + } + + @Override + protected void execute(Event event) { + for (Entity entity : entities.getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + mannequin.setImmovable(immovable); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return new SyntaxStringBuilder(event, debug) + .append("make", entities) + .append(immovable ? "immovable" : "movable") + .toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinParts.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinParts.java new file mode 100644 index 00000000000..12aed1eea51 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/EffMannequinParts.java @@ -0,0 +1,157 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; +import ch.njol.skript.util.Patterns; +import ch.njol.util.Kleenean; +import com.destroystokyo.paper.SkinParts; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.function.Consumer; + +@Name("Mannequin Skin Parts") +@Description("Enable or disable a skin part of a mannequin.") +@Example("enable the mannequin cape for {_mannequin}") +@Example("show the mannequin hat skin part for {_mannequin}") +@Example("reveal the mannequin jacket for {_mannequin}") +@Example("disable the mannequin left pants skin for {_mannequin}") +@Example("hide the mannequin left sleeve for {_mannequin}") +@Example("enable all of the mannequin skin parts for {_mannequin}") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class EffMannequinParts extends Effect { + + private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ + getPattern(MannequinPart.CAPE), + getPattern(MannequinPart.HAT), + getPattern(MannequinPart.JACKET), + getPattern(MannequinPart.LEFT_PANTS), + getPattern(MannequinPart.LEFT_SLEEVE), + getPattern(MannequinPart.RIGHT_PANTS), + getPattern(MannequinPart.RIGHT_SLEEVE), + getPattern(MannequinPart.PANTS), + getPattern(MannequinPart.SLEEVES), + getPattern(MannequinPart.ALL) + }); + + private static Object[] getPattern(MannequinPart part) { + String pattern; + if (part == MannequinPart.ALL) { + pattern = "((enable|show|reveal)|disable:(disable|hide)) all [[of] the] mannequin skin parts (of|for) %entities%"; + } else { + pattern = "((enable|show|reveal)|disable:(disable|hide)) [the] mannequin " + part.part + " [skin [part[s]]] (of|for) %entities%"; + } + return new Object[] {pattern, part}; + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EFFECT, + SyntaxInfo.builder(EffMannequinParts.class) + .addPatterns(PATTERNS.getPatterns()) + .supplier(EffMannequinParts::new) + .build() + ); + } + + public enum MannequinPart { + CAPE("cape"), + HAT("hat"), + JACKET("jacket"), + LEFT_PANTS("left pants"), + LEFT_SLEEVE("left sleeve"), + RIGHT_PANTS("right pants"), + RIGHT_SLEEVE("right sleeve"), + PANTS("pants"), + SLEEVES("sleeves"), + ALL("all"); + + public final String part; + + MannequinPart(String part) { + this.part = part; + } + + } + + private Expression entities; + private boolean enable; + private MannequinPart mannequinPart; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + entities = (Expression) exprs[0]; + enable = !parseResult.hasTag("disable"); + mannequinPart = PATTERNS.getInfo(matchedPattern); + return true; + } + + @Override + protected void execute(Event event) { + Consumer changer = switch (mannequinPart) { + case CAPE -> skin -> skin.setCapeEnabled(enable); + case HAT -> skin -> skin.setHatsEnabled(enable); + case JACKET -> skin -> skin.setJacketEnabled(enable); + case LEFT_PANTS -> skin -> skin.setLeftPantsEnabled(enable); + case LEFT_SLEEVE -> skin -> skin.setLeftSleeveEnabled(enable); + case RIGHT_PANTS -> skin -> skin.setRightPantsEnabled(enable); + case RIGHT_SLEEVE -> skin -> skin.setRightSleeveEnabled(enable); + case PANTS -> skin -> { + skin.setRightPantsEnabled(enable); + skin.setLeftPantsEnabled(enable); + }; + case SLEEVES -> skin -> { + skin.setRightSleeveEnabled(enable); + skin.setLeftSleeveEnabled(enable); + }; + case ALL -> skin -> { + skin.setCapeEnabled(enable); + skin.setHatsEnabled(enable); + skin.setJacketEnabled(enable); + skin.setLeftPantsEnabled(enable); + skin.setLeftSleeveEnabled(enable); + skin.setRightPantsEnabled(enable); + skin.setRightSleeveEnabled(enable); + }; + }; + + for (Entity entity : entities.getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + SkinParts.Mutable skinParts = mannequin.getSkinParts(); + changer.accept(skinParts); + mannequin.setSkinParts(skinParts); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + if (enable) { + builder.append("enable"); + } else { + builder.append("disable"); + } + if (mannequinPart == MannequinPart.ALL) { + builder.append("all of the mannequin skin parts"); + } else { + builder.append("the mannequin", mannequinPart.part); + } + builder.append("of", entities); + return builder.toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinBody.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinBody.java new file mode 100644 index 00000000000..eefeb852f8a --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinBody.java @@ -0,0 +1,108 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +import ch.njol.skript.bukkitutil.NamespacedUtils; +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.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.entity.mannequin.ResolvableProfileBuilder; +import org.skriptlang.skript.bukkit.entity.mannequin.SkinPatchBuilder; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin Body") +@Description(""" + The body texture displayed on a mannequin. + The body key is represented as a namespaced key. + A namespaced key can be formatted as 'namespace:id' or 'id'. \ + It can only contain one ':' to separate the namespace and the id. \ + Only alphanumeric characters, periods, underscores, and dashes can be used. + Example: "minecraft:diamond_axe", namespace is "minecraft, and id is "diamond_axe". + Doing just "diamond_axe" is acceptable as well, as it defaults to the minecraft namespace. + """) +@Example("set the mannequin body of {_mannequin} to \"custom:body\"") +@Example("clear the mannequin body key of last spawned mannequin") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinBody extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinBody.class, + String.class, + "[mannequin] body [texture] (key|id)", + "entities", + false + ).supplier(ExprMannequinBody::new) + .build() + ); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public @Nullable String convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + SkinPatch skinPatch = mannequin.getProfile().skinPatch(); + Key key = skinPatch.body(); + return key == null ? null : key.asString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(String.class); + return null; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Key key = null; + if (delta != null) { + key = NamespacedUtils.getKeyWithErrors(this, (String) delta[0]); + if (key == null) + return; + } + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + ResolvableProfile profile = mannequin.getProfile(); + SkinPatch skinPatch = profile.skinPatch(); + if (skinPatch.body() == key) + continue; + SkinPatch newPatch = new SkinPatchBuilder(skinPatch) + .body(key) + .build(); + ResolvableProfile newProfile = new ResolvableProfileBuilder(profile) + .skinPatch(newPatch) + .build(); + newProfile.resolve(); + mannequin.setProfile(newProfile); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "mannequin body"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinCape.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinCape.java new file mode 100644 index 00000000000..0cbf78d1a13 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinCape.java @@ -0,0 +1,108 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +import ch.njol.skript.bukkitutil.NamespacedUtils; +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.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.entity.mannequin.ResolvableProfileBuilder; +import org.skriptlang.skript.bukkit.entity.mannequin.SkinPatchBuilder; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin Cape") +@Description(""" + The cape texture displayed on a mannequin. + The cape key is represented as a namespaced key. + A namespaced key can be formatted as 'namespace:id' or 'id'. \ + It can only contain one ':' to separate the namespace and the id. \ + Only alphanumeric characters, periods, underscores, and dashes can be used. + Example: "minecraft:diamond_axe", namespace is "minecraft, and id is "diamond_axe". + Doing just "diamond_axe" is acceptable as well, as it defaults to the minecraft namespace. + """) +@Example("set the mannequin cape of {_mannequin} to \"custom:cape\"") +@Example("clear the mannequin cape key of last spawned mannequin") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinCape extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinCape.class, + String.class, + "[mannequin] cape [texture] (key|id)", + "entities", + false + ).supplier(ExprMannequinCape::new) + .build() + ); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public @Nullable String convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + SkinPatch skinPatch = mannequin.getProfile().skinPatch(); + Key key = skinPatch.cape(); + return key == null ? null : key.asString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(String.class); + return null; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Key key = null; + if (delta != null) { + key = NamespacedUtils.getKeyWithErrors(this, (String) delta[0]); + if (key == null) + return; + } + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + ResolvableProfile profile = mannequin.getProfile(); + SkinPatch skinPatch = profile.skinPatch(); + if (skinPatch.cape() == key) + continue; + SkinPatch newPatch = new SkinPatchBuilder(skinPatch) + .cape(key) + .build(); + ResolvableProfile newProfile = new ResolvableProfileBuilder(profile) + .skinPatch(newPatch) + .build(); + newProfile.resolve(); + mannequin.setProfile(newProfile); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "mannequin cape"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinDesc.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinDesc.java new file mode 100644 index 00000000000..a31e284d790 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinDesc.java @@ -0,0 +1,94 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.util.coll.CollectionUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin Description") +@Description(""" + The description of a mannequin. + The description is displayed below the display name of the mannequin. + If the display name of the mannequin is not set, the description will not show. + The default description of a mannequin is "NPC". + """) +@Example("set the mannequin description of {_mannequin} to \"Shop\"") +@Example("clear the mannequin description of last spawned mannequin") +@Example("reset the mannequin description of last spawned mannequin") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinDesc extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinDesc.class, + String.class, + "mannequin description", + "entities", + false + ).supplier(ExprMannequinDesc::new) + .build() + ); + } + + @Override + public @Nullable String convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + Component desc = mannequin.getDescription(); + if (desc == null) + return null; + // TODO: Use Pickle's Adventure Component API + return LegacyComponentSerializer.legacyAmpersand().serialize(desc); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, RESET -> CollectionUtils.array(String.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Component component = null; + if (mode == ChangeMode.SET) { + assert delta != null; + String string = (String) delta[0]; + component = Component.text(string); + } else if (mode == ChangeMode.RESET) { + component = Mannequin.defaultDescription(); + } + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + mannequin.setDescription(component); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "mannequin description"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinElytra.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinElytra.java new file mode 100644 index 00000000000..d5cf2f9bc8d --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinElytra.java @@ -0,0 +1,108 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +import ch.njol.skript.bukkitutil.NamespacedUtils; +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.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.entity.mannequin.ResolvableProfileBuilder; +import org.skriptlang.skript.bukkit.entity.mannequin.SkinPatchBuilder; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin Elytra") +@Description(""" + The elytra texture displayed on a mannequin. + The elytra key is represented as a namespaced key. + A namespaced key can be formatted as 'namespace:id' or 'id'. \ + It can only contain one ':' to separate the namespace and the id. \ + Only alphanumeric characters, periods, underscores, and dashes can be used. + Example: "minecraft:diamond_axe", namespace is "minecraft, and id is "diamond_axe". + Doing just "diamond_axe" is acceptable as well, as it defaults to the minecraft namespace. + """) +@Example("set the mannequin elytra of {_mannequin} to \"custom:elytra\"") +@Example("clear the mannequin elytra key of last spawned mannequin") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinElytra extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinElytra.class, + String.class, + "[mannequin] elytra [texture] (key|id)", + "entities", + false + ).supplier(ExprMannequinElytra::new) + .build() + ); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public @Nullable String convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + SkinPatch skinPatch = mannequin.getProfile().skinPatch(); + Key key = skinPatch.elytra(); + return key == null ? null : key.asString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(String.class); + return null; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Key key = null; + if (delta != null) { + key = NamespacedUtils.getKeyWithErrors(this, (String) delta[0]); + if (key == null) + return; + } + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + ResolvableProfile profile = mannequin.getProfile(); + SkinPatch skinPatch = profile.skinPatch(); + if (skinPatch.elytra() == key) + continue; + SkinPatch newPatch = new SkinPatchBuilder(skinPatch) + .elytra(key) + .build(); + ResolvableProfile newProfile = new ResolvableProfileBuilder(profile) + .skinPatch(newPatch) + .build(); + newProfile.resolve(); + mannequin.setProfile(newProfile); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "mannequin elytra"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinModel.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinModel.java new file mode 100644 index 00000000000..84f99b6518f --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinModel.java @@ -0,0 +1,93 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.SkinPatch; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.bukkit.profile.PlayerTextures.SkinModel; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.entity.mannequin.ResolvableProfileBuilder; +import org.skriptlang.skript.bukkit.entity.mannequin.SkinPatchBuilder; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Mannequin Model") +@Description("The skin model of a mannequin.") +@Example("set the mannequin model to classic") +@Example("set the mannequin skin model to slim") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinModel extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinModel.class, + SkinModel.class, + "mannequin [skin] model", + "entities", + false + ).supplier(ExprMannequinModel::new) + .build() + ); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public @Nullable SkinModel convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + SkinPatch skinPatch = mannequin.getProfile().skinPatch(); + return skinPatch.model(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(SkinModel.class); + return null; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + SkinModel model = delta == null ? null : (SkinModel) delta[0]; + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + ResolvableProfile profile = mannequin.getProfile(); + SkinPatch skinPatch = profile.skinPatch(); + if (skinPatch.model() == model) + continue; + SkinPatch newPatch = new SkinPatchBuilder(skinPatch) + .model(model) + .build(); + ResolvableProfile newProfile = new ResolvableProfileBuilder(profile) + .skinPatch(newPatch) + .build(); + newProfile.resolve(); + mannequin.setProfile(newProfile); + } + } + + @Override + public Class getReturnType() { + return SkinModel.class; + } + + @Override + protected String getPropertyName() { + return "mannequin model"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinSkin.java b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinSkin.java new file mode 100644 index 00000000000..17b2bb42ba7 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/mannequin/elements/ExprMannequinSkin.java @@ -0,0 +1,120 @@ +package org.skriptlang.skript.bukkit.entity.mannequin.elements; + +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.util.coll.CollectionUtils; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.datacomponent.item.ResolvableProfile.Builder; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mannequin; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.UUID; + +@Name("Mannequin Skin") +@Description(""" + The skin of a mannequin. + The skin can be set to an offlineplayer or a texture value of a skin. + """) +@Example("set the mannequin skin of {_mannequin} to offlineplayer(\"Notch\")") +@Example(""" + set the mannequin skin of last spawned mannequin to "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW\ + 5lY3JhZnQubmV0L3RleHR1cmUvYWJjNzNlN2VlNzA4ZTQ3NjU4YWY3YzMzMTZkY2VkNzk5NDUyNTg3NDg1NjM5ODA4ZDQ3OTVkMjNmYzY4NDU1MCJ9fX0=" + """) +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprMannequinSkin extends SimplePropertyExpression { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprMannequinSkin.class, + Object.class, + "mannequin skin", + "entities", + false + ).supplier(ExprMannequinSkin::new) + .build() + ); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public @Nullable Object convert(Entity entity) { + if (!(entity instanceof Mannequin mannequin)) + return null; + ResolvableProfile profile = mannequin.getProfile(); + UUID uuid = profile.uuid(); + if (uuid != null) + return Bukkit.getOfflinePlayer(uuid); + String name = profile.name(); + if (name != null) + return Bukkit.getOfflinePlayer(name); + for (ProfileProperty property : profile.properties()) { + if (property.getName().equals("textures")) + return property.getValue(); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.RESET) + return CollectionUtils.array(OfflinePlayer.class, String.class); + return null; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + ResolvableProfile profile = Mannequin.defaultProfile(); + if (delta != null) { + if (delta[0] instanceof OfflinePlayer player) { + PlayerProfile playerProfile = player.getPlayerProfile(); + playerProfile.complete(); + profile = ResolvableProfile.resolvableProfile(playerProfile); + } else if (delta[0] instanceof String string) { + if (string.length() < 10 || string.contains(" ")) + return; + Builder builder = ResolvableProfile.resolvableProfile(); + ProfileProperty property = new ProfileProperty("textures", string); + builder.addProperty(property); + profile = builder.build(); + } + } + + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof Mannequin mannequin)) + continue; + mannequin.setProfile(profile); + } + } + + @Override + public Class getReturnType() { + return Object.class; + } + + @Override + public Class[] possibleReturnTypes() { + return CollectionUtils.array(OfflinePlayer.class, String.class); + } + + @Override + protected String getPropertyName() { + return "mannequin skin"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/utils/AdventureUtil.java b/src/main/java/org/skriptlang/skript/bukkit/utils/AdventureUtil.java new file mode 100644 index 00000000000..eb5f0d9469b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/utils/AdventureUtil.java @@ -0,0 +1,24 @@ +package org.skriptlang.skript.bukkit.utils; + +import ch.njol.skript.util.chat.MessageComponent; +import net.kyori.adventure.text.Component; + +import java.util.Arrays; +import java.util.List; + +public class AdventureUtil { + + public static Component toComponent(MessageComponent origin) { + Component base; + if (origin.translation != null) { + String[] strings = origin.translation.split(":"); + String key = strings[0]; + base = Component.translatable(key, Arrays.copyOfRange(strings, 1, strings.length, Object[].class)); + } + } + + public static Component toComponent(List messageComponents) { + + } + +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index e9853b2c01a..6035dfa9a89 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -783,6 +783,14 @@ entities: 0: gray trader llama[plural:s] 1: baby:gray trader llama cria[plural:s] + # MannequinData + mannequin: + name: mannequin¦s + patterns: + 0: mannequin[plural:s] + 1: immovable mannequin[plural:s] + 2: movable mannequin[plural:s] + # MinecartData minecart: name: minecart¦s @@ -1593,9 +1601,6 @@ entities: copper golem: name: copper golem¦s pattern: copper golem[plural:s] - mannequin: - name: mannequin¦s - pattern: mannequin[plural:s] # SuperTypes human: @@ -3013,6 +3018,11 @@ damage types: wither: wither wither_skull: wither skull +# -- Skin Models -- +skin models: + classic: classic, classic skin, classic skin model, classic model + slim: slim, slim skin, slim skin model, slim model + # -- Boolean -- boolean: true: @@ -3133,6 +3143,7 @@ types: frogvariant: frog variant¦s @a itemcomponent: item component¦s @an equippablecomponent: equippable component¦s @an + skinmodel: skin model¦s @a # Skript weathertype: weather type¦s @a diff --git a/src/test/skript/tests/misc/EntityData.sk b/src/test/skript/tests/misc/EntityData.sk index b1e02b2826e..1f4b14253b5 100644 --- a/src/test/skript/tests/misc/EntityData.sk +++ b/src/test/skript/tests/misc/EntityData.sk @@ -461,6 +461,11 @@ test "llama data": testData(baby gray trader llama) testData(gray trader llama cria) +test "mannequin data" when running minecraft "1.21.9": + setSuperTypes(mannequin) + testData(immovable mannequin) + testData(movable mannequin) + test "minecart data": setSuperTypes(minecart) testData(minecart) diff --git a/src/test/skript/tests/syntaxes/effects/EffHandedness.sk b/src/test/skript/tests/syntaxes/effects/EffHandedness.sk index 7d55bc1abda..1498f5b67be 100644 --- a/src/test/skript/tests/syntaxes/effects/EffHandedness.sk +++ b/src/test/skript/tests/syntaxes/effects/EffHandedness.sk @@ -9,3 +9,12 @@ test "left handedness": make entity left handed assert entity is left handed with "zombie is not left handed after being made left handed again" delete entity + +test "mannequin handedness" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + make {_entity} right handed + assert {_entity} is right handed with "Mannequin should be right handed" + make {_entity} left handed + assert {_entity} is left handed with "Mannequin should be left handed" diff --git a/src/test/skript/tests/syntaxes/effects/EffMannequinImmovable.sk b/src/test/skript/tests/syntaxes/effects/EffMannequinImmovable.sk new file mode 100644 index 00000000000..9a22ae60294 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffMannequinImmovable.sk @@ -0,0 +1,8 @@ +test "mannequin immovable" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + assert {_entity} is movable with "Mannequin should be movable on spawn" + make {_entity} immovable + assert {_entity} is immovable with "Mannequin should be immovable" + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/effects/EffMannequinParts.sk b/src/test/skript/tests/syntaxes/effects/EffMannequinParts.sk new file mode 100644 index 00000000000..8cdcde46c67 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffMannequinParts.sk @@ -0,0 +1,77 @@ +test "mannequin parts" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + disable the mannequin cape for {_entity} + assert the mannequin cape of {_entity} is disabled with "Mannequin cape should be disabled" + enable the mannequin cape for {_entity} + assert the mannequin cape of {_entity} is enabled with "Mannequin cape should be enabled" + + disable the mannequin hat for {_entity} + assert the mannequin hat of {_entity} is disabled with "Mannequin hat should be disabled" + enable the mannequin hat for {_entity} + assert the mannequin hat of {_entity} is enabled with "Mannequin hat should be enabled" + + disable the mannequin jacket for {_entity} + assert the mannequin jacket of {_entity} is disabled with "Mannequin jacket should be disabled" + enable the mannequin jacket for {_entity} + assert the mannequin jacket of {_entity} is enabled with "Mannequin jacket should be enabled" + + disable the mannequin left pants for {_entity} + assert the mannequin left pants of {_entity} is disabled with "Mannequin left pants should be disabled" + enable the mannequin left pants for {_entity} + assert the mannequin left pants of {_entity} is enabled with "Mannequin left pants should be enabled" + + disable the mannequin left sleeve for {_entity} + assert the mannequin left sleeve of {_entity} is disabled with "Mannequin left sleeve should be disabled" + enable the mannequin left sleeve for {_entity} + assert the mannequin left sleeve of {_entity} is enabled with "Mannequin left sleeve should be enabled" + + disable the mannequin right pants for {_entity} + assert the mannequin right pants of {_entity} is disabled with "Mannequin right pants should be disabled" + enable the mannequin right pants for {_entity} + assert the mannequin right pants of {_entity} is enabled with "Mannequin right pants should be enabled" + + disable the mannequin right sleeve for {_entity} + assert the mannequin right sleeve of {_entity} is disabled with "Mannequin right sleeve should be disabled" + enable the mannequin right sleeve for {_entity} + assert the mannequin right sleeve of {_entity} is enabled with "Mannequin right sleeve should be enabled" + + disable the mannequin pants for {_entity} + assert the mannequin pants of {_entity} is disabled with "Mannequin pants should be disabled" + assert the mannequin left pants of {_entity} is disabled with "Mannequin left pants should be disabled after 'pants'" + assert the mannequin right pants of {_entity} is disabled with "Mannequin right pants should be disabled after 'pants'" + enable the mannequin pants for {_entity} + assert the mannequin pants of {_entity} is enabled with "Mannequin pants should be enabled" + assert the mannequin left pants of {_entity} is enabled with "Mannequin left pants should be enabled after 'pants'" + assert the mannequin right pants of {_entity} is enabled with "Mannequin right pants should be enabled after 'pants'" + + disable the mannequin sleeves for {_entity} + assert the mannequin sleeves of {_entity} is disabled with "Mannequin sleeves should be disabled" + assert the mannequin left sleeve of {_entity} is disabled with "Mannequin left sleeve should be disabled after 'sleeves'" + assert the mannequin right sleeve of {_entity} is disabled with "Mannequin right sleeve should be disabled after 'sleeves'" + enable the mannequin sleeves for {_entity} + assert the mannequin sleeves of {_entity} is enabled with "Mannequin sleeves should be enabled" + assert the mannequin left sleeve of {_entity} is enabled with "Mannequin left sleeve should be enabled after 'sleeves'" + assert the mannequin right sleeve of {_entity} is enabled with "Mannequin right sleeve should be enabled after 'sleeves'" + + disable all of the mannequin skin parts for {_entity} + assert all of the mannequin skin parts of {_entity} are disabled with "All mannequin skin parts should be disabled" + assert the mannequin cape of {_entity} is disabled with "Mannequin cape should be disabled after 'all'" + assert the mannequin hat of {_entity} is disabled with "Mannequin hat should be disabled after 'all'" + assert the mannequin jacket of {_entity} is disabled with "Mannequin jacket should be disabled after 'all'" + assert the mannequin left pants of {_entity} is disabled with "Mannequin left pants should be disabled after 'all'" + assert the mannequin left sleeve of {_entity} is disabled with "Mannequin left sleeve should be disabled after 'all'" + assert the mannequin right pants of {_entity} is disabled with "Mannequin right pants should be disabled after 'all'" + assert the mannequin right sleeve of {_entity} is disabled with "Mannequin right sleeve should be disabled after 'all'" + enable all of the mannequin skin parts for {_entity} + assert all of the mannequin skin parts of {_entity} are enabled with "All mannequin skin parts should be enabled" + assert the mannequin cape of {_entity} is enabled with "Mannequin cape should be enabled after 'all'" + assert the mannequin hat of {_entity} is enabled with "Mannequin hat should be enabled after 'all'" + assert the mannequin jacket of {_entity} is enabled with "Mannequin jacket should be enabled after 'all'" + assert the mannequin left pants of {_entity} is enabled with "Mannequin left pants should be enabled after 'all'" + assert the mannequin left sleeve of {_entity} is enabled with "Mannequin left sleeve should be enabled after 'all'" + assert the mannequin right pants of {_entity} is enabled with "Mannequin right pants should be enabled after 'all'" + assert the mannequin right sleeve of {_entity} is enabled with "Mannequin right sleeve should be enabled after 'all'" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinBody.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinBody.sk new file mode 100644 index 00000000000..ba00c7ad4e4 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinBody.sk @@ -0,0 +1,12 @@ +test "mannequin body" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + loop "custom:body1", "custom:body2" and "custom:body3": + set the mannequin body of {_entity} to loop-value + assert the mannequin body of {_entity} is loop-value with "Mannequin body should be '%loop-value%'" + + clear the mannequin body of {_entity} + assert the mannequin body of {_entity} is not set with "Mannequin body was not cleared" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinCape.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinCape.sk new file mode 100644 index 00000000000..7f4033303a8 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinCape.sk @@ -0,0 +1,12 @@ +test "mannequin cape" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + loop "custom:cape1", "custom:cape2" and "custom:cape3": + set the mannequin cape of {_entity} to loop-value + assert the mannequin cape of {_entity} is loop-value with "Mannequin cape should be '%loop-value%'" + + clear the mannequin cape of {_entity} + assert the mannequin cape of {_entity} is not set with "Mannequin cape was not cleared" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinDesc.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinDesc.sk new file mode 100644 index 00000000000..668f491f197 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinDesc.sk @@ -0,0 +1,12 @@ +test "mannequin description" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + set the mannequin description of {_entity} to "SkriptLang" + assert the mannequin description of {_entity} is "SkriptLang" with "Mannequin description was not set" + clear the mannequin description of {_entity} + assert the mannequin description of {_entity} is not set with "Mannequin description was not cleared" + reset the mannequin description of {_entity} + assert the mannequin description of {_entity} is "NPC" with "Mannequin description was not reset" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinElytra.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinElytra.sk new file mode 100644 index 00000000000..9ad9aa5a1c6 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinElytra.sk @@ -0,0 +1,12 @@ +test "mannequin elytra" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + loop "custom:elytra1", "custom:elytra2" and "custom:elytra3": + set the mannequin elytra of {_entity} to loop-value + assert the mannequin elytra of {_entity} is loop-value with "Mannequin elytra should be '%loop-value%'" + + clear the mannequin elytra of {_entity} + assert the mannequin elytra of {_entity} is not set with "Mannequin elytra was not cleared" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinModel.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinModel.sk new file mode 100644 index 00000000000..a82ecac476f --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinModel.sk @@ -0,0 +1,10 @@ +test "mannequin elytra" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + set the mannequin model of {_entity} to classic + assert the mannequin model of {_entity} is classic with "Mannequin model should be classic" + set the mannequin model of {_entity} to slim + assert the mannequin model of {_entity} is slim with "Mannequin model should be slim" + + clear entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMannequinSkin.sk b/src/test/skript/tests/syntaxes/expressions/ExprMannequinSkin.sk new file mode 100644 index 00000000000..ad7b6a3cd42 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMannequinSkin.sk @@ -0,0 +1,13 @@ +options: + texture: "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWJjNzNlN2VlNzA4ZTQ3NjU4YWY3YzMzMTZkY2VkNzk5NDUyNTg3NDg1NjM5ODA4ZDQ3OTVkMjNmYzY4NDU1MCJ9fX0=" + +test "mannequin skin" when running minecraft "1.21.9": + spawn a mannequin at test-location: + set {_entity} to entity + + set the mannequin skin of {_entity} to test-offline-player + assert the mannequin skin of {_entity} is test-offline-player with "Mannequin skin was not set to test offline player" + set the mannequin skin of {_entity} to {@texture} + assert the mannequin skin of {_entity} is {@texture} with "Mannequin skin was not set to texture value" + + clear entity within {_entity}