diff --git a/src/main/java/ch/njol/skript/registrations/Feature.java b/src/main/java/ch/njol/skript/registrations/Feature.java index e233c12ac01..55d5133d02b 100644 --- a/src/main/java/ch/njol/skript/registrations/Feature.java +++ b/src/main/java/ch/njol/skript/registrations/Feature.java @@ -18,7 +18,8 @@ public enum Feature implements Experiment { CATCH_ERRORS("catch runtime errors", LifeCycle.EXPERIMENTAL, "error catching [section]"), TYPE_HINTS("type hints", LifeCycle.EXPERIMENTAL, "[local variable] type hints"), DAMAGE_SOURCE("damage source", LifeCycle.EXPERIMENTAL, "damage source[s]"), - EQUIPPABLE_COMPONENTS("equippable components", LifeCycle.EXPERIMENTAL, "equippable components") + EQUIPPABLE_COMPONENTS("equippable components", LifeCycle.EXPERIMENTAL, "equippable components"), + BLOCKING_COMPONENTS("blocking components", LifeCycle.EXPERIMENTAL, "blocking components"), ; private final String codeName; diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/ItemComponentModule.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/ItemComponentModule.java index 943e6a3d0bf..5fd75e7c2d4 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/ItemComponentModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/ItemComponentModule.java @@ -7,6 +7,7 @@ import ch.njol.skript.registrations.Classes; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingModule; import org.skriptlang.skript.bukkit.itemcomponents.equippable.EquippableModule; import org.skriptlang.skript.bukkit.itemcomponents.generic.ExprItemCompCopy; @@ -47,7 +48,10 @@ public String toVariableNameString(ComponentWrapper wrapper) { @Override public void load(SkriptAddon addon) { - addon.loadModules(new EquippableModule()); + addon.loadModules( + new EquippableModule(), + new BlockingModule() + ); ExprItemCompCopy.register(addon.syntaxRegistry()); } diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingExperimentalSyntax.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingExperimentalSyntax.java new file mode 100644 index 00000000000..f7b9630e740 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingExperimentalSyntax.java @@ -0,0 +1,20 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking; + +import ch.njol.skript.lang.SyntaxElement; +import ch.njol.skript.registrations.Feature; +import org.skriptlang.skript.lang.experiment.ExperimentData; +import org.skriptlang.skript.lang.experiment.SimpleExperimentalSyntax; + +/** + * Typed {@link SimpleExperimentalSyntax} for {@link SyntaxElement}s that require {@link Feature#BLOCKING_COMPONENTS}. + */ +public interface BlockingExperimentalSyntax extends SimpleExperimentalSyntax { + + ExperimentData EXPERIMENT_DATA = ExperimentData.createSingularData(Feature.BLOCKING_COMPONENTS); + + @Override + default ExperimentData getExperimentData() { + return EXPERIMENT_DATA; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingModule.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingModule.java new file mode 100644 index 00000000000..0f32c57d50b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingModule.java @@ -0,0 +1,173 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking; + +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.ItemSource; +import ch.njol.skript.util.slot.Slot; +import io.papermc.paper.datacomponent.item.BlocksAttacks; +import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction; +import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction; +import org.bukkit.inventory.ItemStack; +import org.skriptlang.skript.addon.AddonModule; +import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.elements.*; +import org.skriptlang.skript.lang.converter.Converter; +import org.skriptlang.skript.lang.converter.Converters; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.Arrays; +import java.util.function.Consumer; + +public class BlockingModule implements AddonModule { + + @Override + public boolean canLoad(SkriptAddon addon) { + return Skript.classExists("io.papermc.paper.datacomponent.item.BlocksAttacks"); + } + + @Override + public void init(SkriptAddon addon) { + Classes.registerClass(new ClassInfo<>(BlockingWrapper.class, "blockingcomponent") + .user("blocking ?components?") + .name("Blocking Component") + .description(""" + Represents a blocking component used for items. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) + .requiredPlugins("Minecraft 1.21.5+") + .since("INSERT VERSION") + .defaultExpression(new EventValueExpression<>(BlockingWrapper.class)) + .parser(new Parser<>() { + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(BlockingWrapper wrapper, int flags) { + return "blocking component"; + } + + @Override + public String toVariableNameString(BlockingWrapper wrapper) { + return "blocking component#" + wrapper.hashCode(); + } + }) + .after("itemstack", "itemtype", "slot") + ); + + Classes.registerClass(new ClassInfo<>(DamageReductionWrapper.class, "damagereduction") + .user("damage ?reductions?") + .name("Damage Reduction") + .description(""" + Represents a damage reduction that is applied to a blocking component. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) + .requiredPlugins("Minecraft 1.21.5+") + .since("INSERT VERSION") + .defaultExpression(new EventValueExpression<>(DamageReductionWrapper.class)) + .parser(new Parser<>() { + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(DamageReductionWrapper wrapper, int flags) { + return "damage reduction"; + } + + @Override + public String toVariableNameString(DamageReductionWrapper wrapper) { + return "damage reduction#" + wrapper.hashCode(); + } + }) + ); + + Classes.registerClass(new ClassInfo<>(DamageFunctionWrapper.class, "itemdamagefunction") + .user("item ?damage ?functions?") + .name("Item Damage Function") + .description(""" + Represents an item damage function that is applied to a blocking component. + Item Damage Functions contain data that attribute to: + - The base amount of damage to be applied to the item, if the attack damage passes the threshold + - The factor amount to get a fraction of the attack damage to be applied to the item, if the attack damage passes the threshold + - The threshold amount + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) + .requiredPlugins("Minecraft 1.21.5+") + .since("INSERT VERSION") + .defaultExpression(new EventValueExpression<>(DamageFunctionWrapper.class)) + .parser(new Parser<>() { + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(DamageFunctionWrapper wrapper, int flags) { + return "item damage function"; + } + + @Override + public String toVariableNameString(DamageFunctionWrapper wrapper) { + return "item damage function#" + wrapper.hashCode(); + } + }) + ); + + Converters.registerConverter(BlocksAttacks.class, BlockingWrapper.class, BlockingWrapper::new, Converter.NO_RIGHT_CHAINING); + Converters.registerConverter(ItemStack.class, BlockingWrapper.class, BlockingWrapper::new, Converter.NO_RIGHT_CHAINING); + Converters.registerConverter(ItemType.class, BlockingWrapper.class, itemType -> new BlockingWrapper(new ItemSource<>(itemType)), Converter.NO_RIGHT_CHAINING); + Converters.registerConverter(Slot.class, BlockingWrapper.class, slot -> { + ItemSource itemSource = ItemSource.fromSlot(slot); + if (itemSource == null) + return null; + return new BlockingWrapper(itemSource); + }, Converter.NO_RIGHT_CHAINING); + + Converters.registerConverter(DamageReduction.class, DamageReductionWrapper.class, DamageReductionWrapper::new, Converter.NO_RIGHT_CHAINING); + Converters.registerConverter(ItemDamageFunction.class, DamageFunctionWrapper.class, DamageFunctionWrapper::new, Converter.NO_RIGHT_CHAINING); + } + + @Override + public void load(SkriptAddon addon) { + register(addon.syntaxRegistry(), + + ExprBlockCompBlockSound::register, + ExprBlockCompBypass::register, + ExprBlockCompDamageFunction::register, + ExprBlockCompDelay::register, + ExprBlockCompDisableScale::register, + ExprBlockCompDisableSound::register, + ExprBlockCompReductions::register, + ExprBlockingComponent::register, + ExprDamageFunctionBase::register, + ExprDamageFunctionFactor::register, + ExprDamageFunctionThreshold::register, + ExprReductionAngle::register, + ExprReductionBase::register, + ExprReductionDamageTypes::register, + ExprReductionFactor::register, + + ExprSecBlankBlockComp::register, + ExprSecDamageFunction::register, + ExprSecReduction::register + ); + } + + private void register(SyntaxRegistry registry, Consumer... consumers) { + Arrays.stream(consumers).forEach(consumer -> consumer.accept(registry)); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingWrapper.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingWrapper.java new file mode 100644 index 00000000000..23145d10cee --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/BlockingWrapper.java @@ -0,0 +1,282 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking; + +import ch.njol.skript.util.ItemSource; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.datacomponent.DataComponentType.Valued; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.BlocksAttacks; +import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction; +import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction; +import io.papermc.paper.registry.tag.TagKey; +import net.kyori.adventure.key.Key; +import org.bukkit.damage.DamageType; +import org.bukkit.inventory.ItemStack; +import org.jspecify.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.ComponentWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper.BlockingBuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * A {@link ComponentWrapper} for getting and setting data on a {@link BlocksAttacks} component. + */ +@SuppressWarnings("UnstableApiUsage") +public class BlockingWrapper extends ComponentWrapper { + + public BlockingWrapper(ItemStack itemStack) { + super(itemStack); + } + + public BlockingWrapper(ItemSource itemSource) { + super(itemSource); + } + + public BlockingWrapper(BlocksAttacks component) { + super(component); + } + + public BlockingWrapper(BlockingBuilder builder) { + super(builder); + } + + @Override + public Valued getDataComponentType() { + return DataComponentTypes.BLOCKS_ATTACKS; + } + + @Override + protected BlocksAttacks getComponent(ItemStack itemStack) { + BlocksAttacks component = itemStack.getData(DataComponentTypes.BLOCKS_ATTACKS); + if (component != null) + return component; + return BlocksAttacks.blocksAttacks().build(); + } + + @Override + protected BlockingBuilder getBuilder(ItemStack itemStack) { + BlocksAttacks component = itemStack.getData(DataComponentTypes.BLOCKS_ATTACKS); + if (component != null) + return new BlockingBuilder(component); + return new BlockingBuilder(); + } + + @Override + protected void setComponent(ItemStack itemStack, BlocksAttacks component) { + itemStack.setData(DataComponentTypes.BLOCKS_ATTACKS, component); + } + + @Override + protected BlockingBuilder getBuilder(BlocksAttacks component) { + return new BlockingBuilder(component); + } + + @Override + public BlockingWrapper clone() { + BlockingWrapper clone = newWrapper(); + BlocksAttacks base = getComponent(); + clone.applyComponent(new BlockingBuilder(base).build()); + return clone; + } + + @Override + public BlocksAttacks newComponent() { + return new BlockingBuilder().build(); + } + + @Override + public BlockingBuilder newBuilder() { + return new BlockingBuilder(); + } + + @Override + public BlockingWrapper newWrapper() { + return newInstance(); + } + + /** + * @return {@link DamageReduction}s in the form of {@link DamageReductionWrapper}s. + */ + public List getDamageReductions() { + List wrappers = new ArrayList<>(); + getComponent().damageReductions().forEach(reduction -> + wrappers.add(new DamageReductionWrapper(this, reduction))); + return wrappers; + } + + /** + * @return {@link ItemDamageFunction} in the form of {@link DamageFunctionWrapper}. + */ + public DamageFunctionWrapper getDamageFunction() { + ItemDamageFunction damageFunction = getComponent().itemDamage(); + return new DamageFunctionWrapper(this, damageFunction); + } + + /** + * Creates a new instance of {@link BlockingWrapper} with a blank {@link BlocksAttacks}. + * @return The new {@link BlockingWrapper}. + */ + public static BlockingWrapper newInstance() { + return new BlockingWrapper(new BlockingBuilder().build()); + } + + /** + * Custom builder class for {@link BlocksAttacks}. + */ + @SuppressWarnings("NonExtendableApiUsage") + public static class BlockingBuilder implements DataComponentBuilder { + + private List damageReductions = new ArrayList<>(); + private float blockDelaySeconds = 0f; + private float disableCooldownScale = 1f; + private ItemDamageFunction damageFunction = ItemDamageFunction.itemDamageFunction().build(); + private @Nullable TagKey bypassedBy = null; + private @Nullable Key blockSound = null; + private @Nullable Key disableSound = null; + + /** + * Construct a new {@link BlockingBuilder}. + */ + public BlockingBuilder() {} + + /** + * Construct a new {@link BlockingBuilder} with the data from {@code component}. + * @param component The {@link BlocksAttacks} to copy data from. + */ + public BlockingBuilder(BlocksAttacks component) { + damageReductions.addAll(new ArrayList<>(component.damageReductions())); + blockDelaySeconds = component.blockDelaySeconds(); + disableCooldownScale = component.disableCooldownScale(); + damageFunction = component.itemDamage(); + bypassedBy = component.bypassedBy(); + blockSound = component.blockSound(); + disableSound = component.disableSound(); + } + + /** + * Add a {@link DamageReduction}. + * @param reduction The {@link DamageReduction} to add. + * @return {@code this} + */ + public BlockingBuilder addDamageReduction(DamageReduction reduction) { + damageReductions.add(reduction); + return this; + } + + /** + * Add {@link DamageReduction}s. + * @param reductions The {@link DamageReduction}s to add. + * @return {@code this} + */ + public BlockingBuilder addDamageReductions(List reductions) { + damageReductions.addAll(reductions); + return this; + } + + /** + * Set {@link DamageReduction}s. + * @param reductions The {@link DamageReduction} to set. + * @return {@code this} + */ + public BlockingBuilder damageReductions(List reductions) { + damageReductions.clear(); + damageReductions.addAll(reductions); + return this; + } + + /** + * Remove a {@link DamageReduction}. + * @param reduction The {@link DamageReduction} to remove. + * @return {@code this} + */ + public BlockingBuilder removeReduction(DamageReduction reduction) { + damageReductions.remove(reduction); + return this; + } + + /** + * Remove {@link DamageReduction}s. + * @param reductions The {@link DamageReduction}s to remove. + * @return {@code this} + */ + public BlockingBuilder removeReductions(List reductions) { + damageReductions.removeAll(reductions); + return this; + } + + /** + * Set the block delay seconds. + * @param blockDelaySeconds The delay. + * @return {@code this} + */ + public BlockingBuilder blockDelaySeconds(float blockDelaySeconds) { + this.blockDelaySeconds = blockDelaySeconds; + return this; + } + + /** + * Set the disable cooldown scale + * @param disableCooldownScale The scale. + * @return {@code this} + */ + public BlockingBuilder disableCooldownScale(float disableCooldownScale) { + this.disableCooldownScale = disableCooldownScale; + return this; + } + + /** + * Set the {@link ItemDamageFunction}. + * @param damageFunction The {@link ItemDamageFunction} to set. + * @return {@code this} + */ + public BlockingBuilder itemDamage(ItemDamageFunction damageFunction) { + this.damageFunction = damageFunction; + return this; + } + + /** + * Set the damage type to bypass. + * @param bypassedBy The damage type to set. + * @return {@code this} + */ + public BlockingBuilder bypassedBy(@Nullable TagKey bypassedBy) { + this.bypassedBy = bypassedBy; + return this; + } + + /** + * Set the block sound. + * @param blockSound The block sound to set. + * @return {@code this} + */ + public BlockingBuilder blockSound(@Nullable Key blockSound) { + this.blockSound = blockSound; + return this; + } + + /** + * Set the disable sound. + * @param disableSound The disable sound to set. + * @return {@code this} + */ + public BlockingBuilder disableSound(@Nullable Key disableSound) { + this.disableSound = disableSound; + return this; + } + + @Override + public BlocksAttacks build() { + return BlocksAttacks.blocksAttacks() + .damageReductions(damageReductions) + .blockDelaySeconds(blockDelaySeconds) + .disableCooldownScale(disableCooldownScale) + .itemDamage(damageFunction) + .bypassedBy(bypassedBy) + .blockSound(blockSound) + .disableSound(disableSound) + .build(); + } + + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageFunctionWrapper.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageFunctionWrapper.java new file mode 100644 index 00000000000..b2553574ba1 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageFunctionWrapper.java @@ -0,0 +1,161 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking; + +import io.papermc.paper.datacomponent.item.BlocksAttacks; +import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction; + +import java.util.function.Consumer; + +/** + * Wrapper for {@link ItemDamageFunction}. + */ +@SuppressWarnings("UnstableApiUsage") +public class DamageFunctionWrapper { + + private BlockingWrapper wrapper; + private ItemDamageFunction damageFunction; + + /** + * Construct a new {@link DamageFunctionWrapper} with the parent {@link BlockingWrapper} and the + * {@link ItemDamageFunction} it belongs to. + * @param wrapper The {@link BlockingWrapper}. + * @param damageFunction The {@link ItemDamageFunction}. + */ + public DamageFunctionWrapper(BlockingWrapper wrapper, ItemDamageFunction damageFunction) { + this.wrapper = wrapper; + this.damageFunction = damageFunction; + } + + /** + * Construct a new {@link DamageFunctionWrapper} with the {@link ItemDamageFunction}. + * @param damageFunction The {@link ItemDamageFunction}. + */ + public DamageFunctionWrapper(ItemDamageFunction damageFunction) { + this.damageFunction = damageFunction; + } + + /** + * Construct a new {@link DamageFunctionWrapper} with a blank {@link ItemDamageFunction}. + */ + public DamageFunctionWrapper() { + this.damageFunction = ItemDamageFunction.itemDamageFunction().build(); + } + + /** + * @return The {@link ItemDamageFunction} this {@link DamageFunctionWrapper} is wrapping. + */ + public ItemDamageFunction getDamageFunction() { + return damageFunction; + } + + /** + * Modify the current {@link ItemDamageFunction} via {@link Builder}. + *

+ * If {@link #wrapper} is not null, and the {@link BlocksAttacks#itemDamage()} equals the {@link ItemDamageFunction} + * before the changes, the {@link ItemDamageFunction} in {@link #wrapper} will be updated. + * If it does not equal, {@link #wrapper} will be set to {@code null}. + *

+ * @param consumer The consumer to change data of the {@link Builder}. + */ + public void modify(Consumer consumer) { + Builder builder = toBuilder(); + consumer.accept(builder); + ItemDamageFunction newFunction = builder.buildFunction(); + if (wrapper != null) { + if (wrapper.getComponent().itemDamage().equals(damageFunction)) { + wrapper.editBuilder(wrapperBuilder -> wrapperBuilder.itemDamage(newFunction)); + } else { + wrapper = null; + } + } + damageFunction = newFunction; + } + + /** + * @return The {@link Builder} with the data from {@link #damageFunction}. + */ + public Builder toBuilder() { + return new Builder(damageFunction); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof DamageFunctionWrapper other)) + return false; + return damageFunction.equals(other.damageFunction); + } + + /** + * Custom builder class for {@link ItemDamageFunction}. + */ + public static class Builder { + + private float base = 0f; + private float factor = 1f; + private float threshold = 1f; + + /** + * Construct a new {@link Builder}. + */ + public Builder() {} + + /** + * Construct a new {@link Builder} and copying the data from {@code damageFunction}. + * @param damageFunction The {@link ItemDamageFunction} to copy data from. + */ + public Builder(ItemDamageFunction damageFunction) { + base = damageFunction.base(); + factor = damageFunction.factor(); + threshold = damageFunction.threshold(); + } + + /** + * Set the base. + * @param base The base amount to set. + * @return {@code this} + */ + public Builder base(float base) { + this.base = base; + return this; + } + + /** + * Set the factor. + * @param factor The factor amount to set. + * @return {@code this} + */ + public Builder factor(float factor) { + this.factor = factor; + return this; + } + + /** + * Set the threshold. + * @param threshold The threshold amount to set. + * @return {@code this} + */ + public Builder threshold(float threshold) { + this.threshold = threshold; + return this; + } + + /** + * @return The finalized {@link ItemDamageFunction}. + */ + public ItemDamageFunction buildFunction() { + return ItemDamageFunction.itemDamageFunction() + .base(base) + .factor(factor) + .threshold(threshold) + .build(); + } + + /** + * @return New {@link DamageFunctionWrapper} with the finalized {@link ItemDamageFunction}. + */ + public DamageFunctionWrapper build() { + return new DamageFunctionWrapper(buildFunction()); + } + + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageReductionWrapper.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageReductionWrapper.java new file mode 100644 index 00000000000..bfef1f189fa --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/DamageReductionWrapper.java @@ -0,0 +1,198 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking; + +import io.papermc.paper.datacomponent.item.BlocksAttacks; +import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.set.RegistryKeySet; +import org.bukkit.damage.DamageType; +import org.checkerframework.checker.index.qual.Positive; +import org.jspecify.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.ComponentUtils; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Wrapper for {@link DamageReduction}. + */ +@SuppressWarnings("UnstableApiUsage") +public class DamageReductionWrapper { + + private BlockingWrapper wrapper; + private DamageReduction damageReduction; + + /** + * Construct a new {@link DamageReductionWrapper} with the parent {@link BlockingWrapper} and the + * {@link DamageReduction} it belongs to. + * @param wrapper The {@link BlockingWrapper}. + * @param damageReduction The {@link DamageReduction}. + */ + public DamageReductionWrapper(BlockingWrapper wrapper, DamageReduction damageReduction) { + this.wrapper = wrapper; + this.damageReduction = damageReduction; + } + + /** + * Construct a new {@link DamageReductionWrapper} with the {@link DamageReduction}. + * @param damageReduction The {@link DamageReduction}. + */ + public DamageReductionWrapper(DamageReduction damageReduction) { + this.damageReduction = damageReduction; + } + + /** + * Construct a new {@link DamageReductionWrapper} with a blank {@link DamageReduction}. + */ + public DamageReductionWrapper() { + this.damageReduction = new Builder().buildDamageReduction(); + } + + /** + * @return The {@link DamageReduction} this {@link DamageReductionWrapper} is wrapping. + */ + public DamageReduction getDamageReduction() { + return damageReduction; + } + + /** + * Modify the current {@link DamageReduction} via {@link Builder}. + *

+ * If {@link #wrapper} is not null, and the {@link BlocksAttacks#damageReductions()} contains the {@link DamageReduction} + * before the changes, the {@link DamageReduction} in {@link #wrapper} will be updated. + * If it does not equal, {@link #wrapper} will be set to {@code null}. + *

+ * @param consumer The consumer to change data of the {@link Builder}. + */ + public void modify(Consumer consumer) { + Builder builder = toBuilder(); + consumer.accept(builder); + DamageReduction newDamageReduction = builder.buildDamageReduction(); + if (wrapper != null) { + if (wrapper.getComponent().damageReductions().contains(damageReduction)) { + wrapper.editBuilder(wrapperBuilder -> { + wrapperBuilder.removeReduction(damageReduction); + wrapperBuilder.addDamageReduction(newDamageReduction); + }); + } else { + wrapper = null; + } + } + damageReduction = newDamageReduction; + } + + /** + * @return The {@link Builder} with the data from {@link #damageReduction}. + */ + public Builder toBuilder() { + return new Builder(damageReduction); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof DamageReductionWrapper other)) + return false; + return damageReduction.equals(other.damageReduction); + } + + /** + * Custom builder class for {@link DamageReduction}. + */ + public static class Builder { + + private float base = 0f; + private float factor = 0f; + private float horizontalBlockingAngle = 90f; + private @Nullable RegistryKeySet types = null; + + /** + * Construct a new {@link Builder}. + */ + public Builder() {} + + /** + * Construct a new {@link Builder} and copying the data from {@code damageReduction}. + * @param damageReduction The {@link DamageReduction} to copy data from. + */ + public Builder(DamageReduction damageReduction) { + base = damageReduction.base(); + factor = damageReduction.factor(); + horizontalBlockingAngle = damageReduction.horizontalBlockingAngle(); + types = damageReduction.type(); + } + + /** + * Set the base. + * @param base The base amount to set. + * @return {@code this} + */ + public Builder base(float base) { + this.base = base; + return this; + } + + /** + * Set the factor. + * @param factor The factor amount to set. + * @return {@code this} + */ + public Builder factor(float factor) { + this.factor = factor; + return this; + } + + /** + * Set the damage types. + * @param types The damage types to set. + * @return {@code this} + */ + public Builder types(@Nullable RegistryKeySet types) { + this.types = types; + return this; + } + + /** + * Set the damage types. + * @param types The damage types to set. + * @return {@code this} + */ + public Builder types(@Nullable List types) { + if (types == null || types.isEmpty()) { + this.types = null; + } else { + this.types = ComponentUtils.collectionToRegistryKeySet(types, RegistryKey.DAMAGE_TYPE); + } + return this; + } + + /** + * Set the horizontal blocking angle. + * @param horizontalBlockingAngle The horizontal blocking aingle to set. + * @return {@code this} + */ + public Builder horizontalBlockingAngle(@Positive float horizontalBlockingAngle) { + this.horizontalBlockingAngle = horizontalBlockingAngle; + return this; + } + + /** + * @return The finalized {@link DamageReduction}. + */ + public DamageReduction buildDamageReduction() { + return DamageReduction.damageReduction() + .base(base) + .factor(factor) + .horizontalBlockingAngle(horizontalBlockingAngle) + .type(types) + .build(); + } + + /** + * @return New {@link DamageReductionWrapper} with the finalized {@link DamageReduction}. + */ + public DamageReductionWrapper build() { + return new DamageReductionWrapper(buildDamageReduction()); + } + + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBlockSound.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBlockSound.java new file mode 100644 index 00000000000..28d10f1ef08 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBlockSound.java @@ -0,0 +1,87 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.bukkitutil.SoundUtils; +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.key.Key; +import org.bukkit.Registry; +import org.bukkit.Sound; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Blocked Sound") +@Description(""" + The sound that plays when the item successfully blocks an attack. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_sound} to the blocked sound of {_item}") +@Example("set the blocked sound of {_item} to \"minecraft:ui.toast.challenge_complete\"") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompBlockSound extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder(ExprBlockCompBlockSound.class, String.class, "blocked sound[s]", "blockingcomponents", true) + .supplier(ExprBlockCompBlockSound::new) + .build() + ); + } + + @Override + public @Nullable String convert(BlockingWrapper wrapper) { + //noinspection UnstableApiUsage + Key key = wrapper.getComponent().blockSound(); + return key == null ? null : key.toString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(String.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Sound enumSound = null; + if (delta != null) { + String soundString = (String) delta[0]; + enumSound = SoundUtils.getSound(soundString); + if (enumSound == null) { + error("Could not find a sound with the id '" + soundString + "'."); + return; + } + } + Key key; + if (enumSound != null) { + key = Registry.SOUNDS.getKey(enumSound); + } else { + key = null; + } + + getExpr().stream(event).forEach(wrapper -> + wrapper.editBuilder(builder -> builder.blockSound(key))); + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "blocked sound"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBypass.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBypass.java new file mode 100644 index 00000000000..2e5934e96c6 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompBypass.java @@ -0,0 +1,86 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.registry.RegistryKey; +import io.papermc.paper.registry.tag.TagKey; +import org.bukkit.Registry; +import org.bukkit.damage.DamageType; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Damage Type Bypass") +@Description(""" + The damage type that can bypass when the item is blocking. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_bypass} to the damage type bypass of {_item}") +@Example("set the damage type bypass of {_item} to magic") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompBypass extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockCompBypass.class, + DamageType.class, + "[blocking] damage type bypass[es]", + "blockingcomponents", + true + ) + .supplier(ExprBlockCompBypass::new) + .build() + ); + } + + @Override + public @Nullable DamageType convert(BlockingWrapper wrapper) { + TagKey damageKey = wrapper.getComponent().bypassedBy(); + if (damageKey == null) + return null; + return Registry.DAMAGE_TYPE.get(damageKey.key()); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(DamageType.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + DamageType provided = delta == null ? null : (DamageType) delta[0]; + TagKey key; + if (provided != null) { + key = TagKey.create(RegistryKey.DAMAGE_TYPE, provided.key()); + } else { + key = null; + } + + getExpr().stream(event).forEach(wrapper -> + wrapper.editBuilder(builder -> builder.bypassedBy(key))); + } + + @Override + public Class getReturnType() { + return DamageType.class; + } + + @Override + protected String getPropertyName() { + return "blocking damage type bypass"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDamageFunction.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDamageFunction.java new file mode 100644 index 00000000000..3c3e3f7c3a2 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDamageFunction.java @@ -0,0 +1,75 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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 org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageFunctionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Item Damage Function") +@Description(""" + The item damage function of a blocking component. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_damageFunction} to the damage function of {_item}") +@Example("set the item damage function of {_item} to a custom damage function") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompDamageFunction extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockCompDamageFunction.class, + DamageFunctionWrapper.class, + "[item] damage function[s]", + "blockingcomponents", + true + ) + .supplier(ExprBlockCompDamageFunction::new) + .build() + ); + } + + @Override + public @Nullable DamageFunctionWrapper convert(BlockingWrapper wrapper) { + return wrapper.getDamageFunction(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET) + return CollectionUtils.array(DamageFunctionWrapper.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + assert delta != null; + DamageFunctionWrapper functionWrapper = (DamageFunctionWrapper) delta[0]; + + getExpr().stream(event).forEach(wrapper -> + wrapper.editBuilder(builder -> builder.itemDamage(functionWrapper.getDamageFunction()))); + } + + @Override + public Class getReturnType() { + return DamageFunctionWrapper.class; + } + + @Override + protected String getPropertyName() { + return "item damage function"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDelay.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDelay.java new file mode 100644 index 00000000000..11d74b9d4b6 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDelay.java @@ -0,0 +1,79 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Delay Time") +@Description(""" + The amount of time before an item can block an attack after starting to block. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_time} to the blocking delay time of {_item}") +@Example("set the blocking delay time of {_item} to 5 ticks") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompDelay extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder(ExprBlockCompDelay.class, Timespan.class, "blocking delay time[s]", "blockingcomponents", true) + .supplier(ExprBlockCompDelay::new) + .build() + ); + } + + @Override + public @Nullable Timespan convert(BlockingWrapper wrapper) { + return new Timespan(TimePeriod.SECOND, (long) wrapper.getComponent().blockDelaySeconds()); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(Timespan.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Timespan) delta[0]).getAs(TimePeriod.SECOND); + + getExpr().stream(event).forEach(wrapper -> { + float current = wrapper.getComponent().blockDelaySeconds(); + switch (mode) { + case SET, DELETE -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newDelay = Math2.fit(0, current, Float.MAX_VALUE); + wrapper.editBuilder(builder -> builder.blockDelaySeconds(newDelay)); + }); + } + + @Override + public Class getReturnType() { + return Timespan.class; + } + + @Override + protected String getPropertyName() { + return "blocking delay time"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableScale.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableScale.java new file mode 100644 index 00000000000..7932c2ea01c --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableScale.java @@ -0,0 +1,88 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Disable Cooldown Scale") +@Description(""" + The scalar applied to the disabled cooldown time for the item when disabled by an attack. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_scale} to the disabled cooldown scale of {_item}") +@Example("set the blocking disable cooldown scale of {_item} to 2") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompDisableScale extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + static { + registerDefault(ExprBlockCompDisableScale.class, Float.class, "[blocking] disabl(e[d]|ing) cooldown (scale|scalar)[s]", + "blockingcomponents"); + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockCompDisableScale.class, + Float.class, + "[blocking] disabl(e[d]|ing) cooldown (scale|scalar)[s]", + "blockingcomponents", + true + ) + .supplier(ExprBlockCompDisableScale::new) + .build() + ); + } + + @Override + public @Nullable Float convert(BlockingWrapper wrapper) { + return wrapper.getComponent().disableCooldownScale(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(wrapper -> { + float current = wrapper.getComponent().disableCooldownScale(); + switch (mode) { + case SET, DELETE -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newScale = Math2.fit(0, current, Float.MAX_VALUE); + wrapper.editBuilder(builder -> builder.disableCooldownScale(newScale)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "blocking disable cooldown scale"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableSound.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableSound.java new file mode 100644 index 00000000000..0ca03eace0c --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompDisableSound.java @@ -0,0 +1,89 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.bukkitutil.SoundUtils; +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.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import net.kyori.adventure.key.Key; +import org.bukkit.Registry; +import org.bukkit.Sound; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component - Disable Sound") +@Description(""" + The sound that plays when an item goes on its disabled cooldown due to an attack. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_sound} to the blocking disable sound of {_item}") +@Example("set the disabled sound of {_item} to \"minecraft:ui.toast.challenge_complete\"") +@RequiredPlugins("Minecraft 1.21.5+") +public class ExprBlockCompDisableSound extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockCompDisableSound.class, + String.class, + "[blocking] disable[d] sound[s]", + "blockingcomponents", + true + ) + .supplier(ExprBlockCompDisableSound::new) + .build() + ); + } + + @Override + public @Nullable String convert(BlockingWrapper wrapper) { + return wrapper.getComponent().disableSound().toString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) + return CollectionUtils.array(String.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Sound enumSound = null; + if (delta != null) { + String soundString = (String) delta[0]; + enumSound = SoundUtils.getSound(soundString); + if (enumSound == null) { + error("Could not find a sound with the id '" + soundString + "'."); + return; + } + } + Key key; + if (enumSound != null) { + key = Registry.SOUNDS.getKey(enumSound); + } else { + key = null; + } + + getExpr().stream(event).forEach(wrapper -> + wrapper.editBuilder(builder -> builder.disableSound(key))); + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "blocking disabled sound"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompReductions.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompReductions.java new file mode 100644 index 00000000000..8505ade9d86 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockCompReductions.java @@ -0,0 +1,120 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.PropertyExpression; +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 ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.ArrayList; +import java.util.List; + +@Name("Blocking Component - Damage Reductions") +@Description(""" + The damage reductions applied to the item. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_reductions::*} to the damage reductions of {_item}") +@Example(""" + set {_reduction} to a custom damage reduction + add {_reduction} to the damage reductions of {_item} + """) +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockCompReductions extends PropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockCompReductions.class, + DamageReductionWrapper.class, + "damage reductions", + "blockingcomponents", + true + ) + .supplier(ExprBlockCompReductions::new) + .build() + ); + } + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + setExpr((Expression) exprs[0]); + return true; + } + + @Override + protected DamageReductionWrapper[] get(Event event, BlockingWrapper[] source) { + List reductionWrappers = new ArrayList<>(); + for (BlockingWrapper wrapper : source) + reductionWrappers.addAll(wrapper.getDamageReductions()); + return reductionWrappers.toArray(DamageReductionWrapper[]::new); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(DamageReductionWrapper[].class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + List provided = new ArrayList<>(); + if (delta != null) { + for (Object object : delta) { + if (object instanceof DamageReductionWrapper wrapper) + provided.add(wrapper.getDamageReduction()); + } + } + + getExpr().stream(event).forEach(wrapper -> + wrapper.editBuilder(builder -> { + switch (mode) { + case SET, DELETE -> builder.damageReductions(provided); + case ADD -> builder.addDamageReductions(provided); + case REMOVE -> builder.removeReductions(provided); + } + })); + } + + @Override + public boolean isSingle() { + return false; + } + + @Override + public Class getReturnType() { + return DamageReductionWrapper.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return new SyntaxStringBuilder(event, debug) + .append("the damage reductions of", getExpr()) + .toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockingComponent.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockingComponent.java new file mode 100644 index 00000000000..171ebebc09b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprBlockingComponent.java @@ -0,0 +1,130 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.aliases.ItemData; +import ch.njol.skript.aliases.ItemType; +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.ItemSource; +import ch.njol.skript.util.slot.Slot; +import ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.BlocksAttacks; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Blocking Component") +@Description(""" + The blocking component of an item. Any changes made to the blocking component will be present on the item. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_component} to the blocking component of {_item} + set the damage type bypass of {_component} to magic + """) +@Example("clear the blocking component of {_item}") +@Example("reset the blocking component of {_item}") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprBlockingComponent extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + static { + register(ExprBlockingComponent.class, BlockingWrapper.class, + "blocking component[s]", "slots/itemtypes"); + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprBlockingComponent.class, + BlockingWrapper.class, + "blocking component[s]", + "slots/itemtypes", + false + ) + .supplier(ExprBlockingComponent::new) + .build() + ); + } + + @Override + public BlockingWrapper convert(Object object) { + ItemSource itemSource = null; + if (object instanceof ItemType itemType) { + itemSource = new ItemSource<>(itemType); + } else if (object instanceof Slot slot) { + itemSource = ItemSource.fromSlot(slot); + } + return itemSource == null ? null : new BlockingWrapper(itemSource); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, RESET -> CollectionUtils.array(BlockingWrapper.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + BlocksAttacks component = null; + if (delta != null) + component = ((BlockingWrapper) delta[0]).getComponent(); + + for (Object object : getExpr().getArray(event)) { + if (object instanceof ItemType itemType) { + changeItemType(itemType, mode, component); + } else if (object instanceof Slot slot) { + changeSlot(slot, mode, component); + } + } + } + + public void changeItemType(ItemType itemType, ChangeMode mode, BlocksAttacks component) { + for (ItemData itemData : itemType) { + ItemStack dataStack = itemData.getStack(); + if (dataStack == null) + continue; + changeItemStack(dataStack, mode, component); + } + } + + public void changeSlot(Slot slot, ChangeMode mode, BlocksAttacks component) { + ItemStack itemStack = slot.getItem(); + if (itemStack == null) + return; + itemStack = changeItemStack(itemStack, mode, component); + slot.setItem(itemStack); + } + + @SuppressWarnings("UnstableApiUsage") + public ItemStack changeItemStack(ItemStack itemStack, ChangeMode mode, BlocksAttacks component) { + switch (mode) { + case SET -> itemStack.setData(DataComponentTypes.BLOCKS_ATTACKS, component); + case DELETE -> itemStack.unsetData(DataComponentTypes.BLOCKS_ATTACKS); + case RESET -> itemStack.resetData(DataComponentTypes.BLOCKS_ATTACKS); + } + return itemStack; + } + + @Override + public Class getReturnType() { + return BlockingWrapper.class; + } + + @Override + protected String getPropertyName() { + return "blocking component"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionBase.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionBase.java new file mode 100644 index 00000000000..f5ac66c5659 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionBase.java @@ -0,0 +1,102 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageFunctionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item Damage Function - Base Amount") +@Description(""" + The base amount of damage to be applied to the item when the item blocks an attack and the dealt damage of the attack \ + passes the threshold amount. + Item Damage Functions contain data that attribute to: + - The base amount of damage to be applied to the item, if the attack damage passes the threshold + - The factor amount to get a fraction of the attack damage to be applied to the item, if the attack damage passes the threshold + - The threshold amount + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_amount} to the item damage function base amount of {_item}") +@Example("set the damage function base of {_item} to 5") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprDamageFunctionBase extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprDamageFunctionBase.class, + Float.class, + "[item] damage function base [amount[s]]", + "blockingcomponents/itemdamagefunctions", + true + ) + .supplier(ExprDamageFunctionBase::new) + .build() + ); + } + + @Override + public @Nullable Float convert(Object object) { + if (object instanceof DamageFunctionWrapper wrapper) { + return wrapper.getDamageFunction().base(); + } else if (object instanceof BlockingWrapper wrapper) { + return wrapper.getComponent().itemDamage().base(); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(object -> { + DamageFunctionWrapper functionWrapper; + if (object instanceof DamageFunctionWrapper wrapper) { + functionWrapper = wrapper; + } else if (object instanceof BlockingWrapper wrapper) { + functionWrapper = wrapper.getDamageFunction(); + } else { + return; + } + float current = functionWrapper.getDamageFunction().base(); + switch (mode) { + case SET, DELETE -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newBase = Math2.fit(0, current, Float.MAX_VALUE); + functionWrapper.modify(builder -> builder.base(newBase)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "item damage function base amount"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionFactor.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionFactor.java new file mode 100644 index 00000000000..00f16d4083d --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionFactor.java @@ -0,0 +1,102 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageFunctionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item Damage Function - Factor Amount") +@Description(""" + The factor amount to get the fraction of the dealt damage to be applied to the item when item blocks an attack \ + and the dealt damage of the attack passes the threshold amount. + Item Damage Functions contain data that attribute to: + - The base amount of damage to be applied to the item, if the attack damage passes the threshold + - The factor amount to get a fraction of the attack damage to be applied to the item, if the attack damage passes the threshold + - The threshold amount + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_amount} to the item damage function factor amount of {_item}") +@Example("set the damage function factor of {_item} to 0.2") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprDamageFunctionFactor extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprDamageFunctionFactor.class, + Float.class, + "[item] damage function factor [amount[s]]", + "blockingcomponents/itemdamagefunctions", + true + ) + .supplier(ExprDamageFunctionFactor::new) + .build() + ); + } + + @Override + public @Nullable Float convert(Object object) { + if (object instanceof DamageFunctionWrapper wrapper) { + return wrapper.getDamageFunction().factor(); + } else if (object instanceof BlockingWrapper wrapper) { + return wrapper.getComponent().itemDamage().factor(); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(object -> { + DamageFunctionWrapper functionWrapper; + if (object instanceof DamageFunctionWrapper wrapper) { + functionWrapper = wrapper; + } else if (object instanceof BlockingWrapper wrapper) { + functionWrapper = wrapper.getDamageFunction(); + } else { + return; + } + float current = functionWrapper.getDamageFunction().factor(); + switch (mode) { + case SET, DELETE -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newFactor = Math2.fit(0, current, Float.MAX_VALUE); + functionWrapper.modify(builder -> builder.factor(newFactor)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "item damage function factor amount"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionThreshold.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionThreshold.java new file mode 100644 index 00000000000..77b93254b87 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprDamageFunctionThreshold.java @@ -0,0 +1,102 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageFunctionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item Damage Function - Threshold Amount") +@Description(""" + The minimum amount of damage required from blocking an attack with the item to deal the 'base' and 'factor' \ + amount of damage to the item. + Item Damage Functions contain data that attribute to: + - The base amount of damage to be applied to the item, if the attack damage passes the threshold + - The factor amount to get a fraction of the attack damage to be applied to the item, if the attack damage passes the threshold + - The threshold amount + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example("set {_amount} to the item damage function threshold amount of {_item}") +@Example("set the damage function threshold of {_item} to 100") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprDamageFunctionThreshold extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprDamageFunctionThreshold.class, + Float.class, + "[item] damage function threshold [amount[s]]", + "blockingcomponents/itemdamagefunctions", + true + ) + .supplier(ExprDamageFunctionThreshold::new) + .build() + ); + } + + @Override + public @Nullable Float convert(Object object) { + if (object instanceof DamageFunctionWrapper wrapper) { + return wrapper.getDamageFunction().threshold(); + } else if (object instanceof BlockingWrapper wrapper) { + return wrapper.getComponent().itemDamage().threshold(); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(object -> { + DamageFunctionWrapper functionWrapper; + if (object instanceof DamageFunctionWrapper wrapper) { + functionWrapper = wrapper; + } else if (object instanceof BlockingWrapper wrapper) { + functionWrapper = wrapper.getDamageFunction(); + } else { + return; + } + float current = functionWrapper.getDamageFunction().threshold(); + switch (mode) { + case SET, DELETE -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newThreshold = Math2.fit(0, current, Float.MAX_VALUE); + functionWrapper.modify(builder -> builder.threshold(newThreshold)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "item damage function threshold amount"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionAngle.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionAngle.java new file mode 100644 index 00000000000..5ebcf5ebc09 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionAngle.java @@ -0,0 +1,93 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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 org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Damage Reduction - Angle") +@Description(""" + The angle in which the item can block the attack. + The angle is dependant on the direction the player is looking when blocking. + The direction the player is looking is considered 0 degrees. If the angle is set to 90, then the area that can be blocked \ + is 45 degrees left and 45 degrees right around the player, from where the player is looking. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_reductions::*} to the damage reductions of {_item} + set {_angles::*} to the reduction angles of {_reductions::*} + """) +@Example("set the damage reduction angles of (the damage reductions of {_item}) to 100") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprReductionAngle extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprReductionAngle.class, + Float.class, + "[damage] reduction [block[ing]] angle[s]", + "damagereductions", + true + ) + .supplier(ExprReductionAngle::new) + .build() + ); + } + + @Override + public @Nullable Float convert(DamageReductionWrapper wrapper) { + return wrapper.getDamageReduction().horizontalBlockingAngle(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, REMOVE, ADD, RESET -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(wrapper -> { + float current = wrapper.getDamageReduction().horizontalBlockingAngle(); + switch (mode) { + case SET, RESET -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newAngle = Math.abs(current); + wrapper.modify(builder -> builder.horizontalBlockingAngle(newAngle)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "damage reduction angle"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionBase.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionBase.java new file mode 100644 index 00000000000..0b513240b5d --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionBase.java @@ -0,0 +1,90 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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 org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Damage Reduction - Base Amount") +@Description(""" + The base amount of damage to block when the item blocks an attack. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_reductions::*} to the damage reductions of {_item} + set {_amounts::*} to the reduction base amounts of {_reductions::*} + """) +@Example("set the damage reduction base of (the damage reductions of {_item}) to 100") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprReductionBase extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprReductionBase.class, + Float.class, + "[damage] reduction base [amount[s]]", + "damagereductions", + true + ) + .supplier(ExprReductionBase::new) + .build() + ); + } + + @Override + public @Nullable Float convert(DamageReductionWrapper wrapper) { + return wrapper.getDamageReduction().base(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, REMOVE, ADD, RESET -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(wrapper -> { + float current = wrapper.getDamageReduction().base(); + switch (mode) { + case SET, RESET -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newBase = current; + wrapper.modify(builder -> builder.base(newBase)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "damage reduction base amount"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionDamageTypes.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionDamageTypes.java new file mode 100644 index 00000000000..4e29e3f9e27 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionDamageTypes.java @@ -0,0 +1,133 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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.PropertyExpression; +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 ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.registry.set.RegistryKeySet; +import org.bukkit.Registry; +import org.bukkit.damage.DamageType; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.ComponentUtils; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.ArrayList; +import java.util.List; + +@Name("Damage Reduction - Damage Types") +@Description(""" + The damage types to which the damage reduction can block using the 'base' and 'factor' amounts. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_reductions::*} to the damage reductions of {_item} + set {_types::*} to the reduction damage types of {_reductions::1} + """) +@Example(""" + set {_reductions::*} to the damage reductions of {_item} + set the damage reduction damage types of {_reductions::1} to magic + """) +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprReductionDamageTypes extends PropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprReductionDamageTypes.class, + DamageType.class, + "[damage] reduction damage types", + "damagereductions", + true + ) + .supplier(ExprReductionDamageTypes::new) + .build() + ); + } + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + setExpr((Expression) exprs[0]); + return true; + } + + @Override + protected DamageType[] get(Event event, DamageReductionWrapper[] source) { + List types = new ArrayList<>(); + for (DamageReductionWrapper wrapper : source) { + RegistryKeySet current = wrapper.getDamageReduction().type(); + types.addAll(ComponentUtils.registryKeySetToCollection(current, Registry.DAMAGE_TYPE)); + } + return types.toArray(DamageType[]::new); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, REMOVE, ADD -> CollectionUtils.array(DamageType[].class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + List provided = new ArrayList<>(); + if (delta != null) { + for (Object object : delta) { + if (object instanceof DamageType damageType) + provided.add(damageType); + } + } + + getExpr().stream(event).forEach(wrapper -> { + RegistryKeySet types = wrapper.getDamageReduction().type(); + List current = new ArrayList<>(ComponentUtils.registryKeySetToCollection(types, Registry.DAMAGE_TYPE)); + switch (mode) { + case SET -> { + current.clear(); + current.addAll(provided); + } + case ADD -> current.addAll(provided); + case REMOVE -> current.removeAll(provided); + case DELETE -> current.clear(); + } + wrapper.modify(builder -> builder.types(current)); + }); + } + + @Override + public boolean isSingle() { + return false; + } + + @Override + public Class getReturnType() { + return DamageType.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return new SyntaxStringBuilder(event, debug) + .append("the damage reduction damage types of", getExpr()) + .toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionFactor.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionFactor.java new file mode 100644 index 00000000000..e46304b0c7e --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprReductionFactor.java @@ -0,0 +1,90 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.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 org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Damage Reduction - Factor Amount") +@Description(""" + The factor amount to get the fraction of the damage of an attack to block when the item blocks an attack. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_reductions::*} to the damage reductions of {_item} + set {_amounts::*} to the reduction factor amounts of {_reductions::*} + """) +@Example("set the damage reduction factor of (the damage reductions of {_item}) to 0.5") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprReductionFactor extends SimplePropertyExpression implements BlockingExperimentalSyntax { + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprReductionFactor.class, + Float.class, + "[damage] reduction factor [amount[s]]", + "damagereductions", + true + ) + .supplier(ExprReductionFactor::new) + .build() + ); + } + + @Override + public @Nullable Float convert(DamageReductionWrapper wrapper) { + return wrapper.getDamageReduction().factor(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, REMOVE, ADD, RESET -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + float provided = delta == null ? 0f : ((Number) delta[0]).floatValue(); + + getExpr().stream(event).forEach(wrapper -> { + float current = wrapper.getDamageReduction().factor(); + switch (mode) { + case SET, RESET -> current = provided; + case ADD -> current += provided; + case REMOVE -> current -= provided; + } + float newFactor = current; + wrapper.modify(builder -> builder.factor(newFactor)); + }); + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "damage reduction factor amount"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecBlankBlockComp.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecBlankBlockComp.java new file mode 100644 index 00000000000..7937b6e1173 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecBlankBlockComp.java @@ -0,0 +1,119 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.config.SectionNode; +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.SectionExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Trigger; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.SectionUtils; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.variables.Variables; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingWrapper; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +@Name("Blank Blocking Component") +@Description(""" + Gets a blank blocking component. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_component} to a blank blocking component: + set the blocked sound to "" + set the disabled sound to "" + set the damage type bypass to magic + set the blocking delay time to 1 second + set the disabled cooldown scale to 0.2 + add (a blank damage reduction) to the damage reductions + set the blocking component of {_item} to {_component} + """) +@Example("clear the blocking component of {_item}") +@Example("reset the blocking component of {_item}") +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprSecBlankBlockComp extends SectionExpression implements BlockingExperimentalSyntax { + + private static class BlockCompSectionEvent extends Event { + + private final BlockingWrapper wrapper; + + public BlockCompSectionEvent(BlockingWrapper wrapper) { + this.wrapper = wrapper; + } + + public BlockingWrapper getWrapper() { + return wrapper; + } + + @Override + public @NotNull HandlerList getHandlers() { + throw new IllegalStateException(); + } + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + SyntaxInfo.Expression.builder(ExprSecBlankBlockComp.class, BlockingWrapper.class) + .addPatterns("a (blank|empty) blocking component") + .supplier(ExprSecBlankBlockComp::new) + .build() + ); + EventValues.registerEventValue(BlockCompSectionEvent.class, BlockingWrapper.class, BlockCompSectionEvent::getWrapper); + } + + private Trigger trigger; + + @Override + public boolean init(Expression[] exprs, int pattern, Kleenean delayed, ParseResult result, @Nullable SectionNode node, @Nullable List triggerItems) { + if (node != null) { + AtomicBoolean isDelayed = new AtomicBoolean(false); + trigger = SectionUtils.loadLinkedCode("blank blocking component", (beforeLoading, afterLoading) -> + loadCode(node, "blank blocking component", beforeLoading, afterLoading, BlockCompSectionEvent.class) + ); + return trigger != null; + } + return true; + } + + @Override + protected BlockingWrapper @Nullable [] get(Event event) { + BlockingWrapper wrapper = BlockingWrapper.newInstance(); + if (trigger != null) { + BlockCompSectionEvent sectionEvent = new BlockCompSectionEvent(wrapper); + Variables.withLocalVariables(event, sectionEvent, () -> TriggerItem.walk(trigger, sectionEvent)); + } + return new BlockingWrapper[] {wrapper}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return BlockingWrapper.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "a blank blocking component"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecDamageFunction.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecDamageFunction.java new file mode 100644 index 00000000000..ed3ecd14e92 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecDamageFunction.java @@ -0,0 +1,114 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.config.SectionNode; +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.SectionExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Trigger; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.SectionUtils; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.variables.Variables; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageFunctionWrapper; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +@Name("Custom Item Damage Function") +@Description(""" + Gets a custom item damage function. + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_damageFunction} to a custom item damage function: + set the damage function base to 10 + set the damage function factor to 0.3 + set the damage function threshold to 50 + set the item damage function of {_item} to {_damageFunction} + """) +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprSecDamageFunction extends SectionExpression implements BlockingExperimentalSyntax { + + private static class DamageFunctionSectionEvent extends Event { + + private final DamageFunctionWrapper wrapper; + + public DamageFunctionSectionEvent(DamageFunctionWrapper wrapper) { + this.wrapper = wrapper; + } + + public DamageFunctionWrapper getWrapper() { + return wrapper; + } + + @Override + public @NotNull HandlerList getHandlers() { + throw new IllegalStateException(); + } + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + SyntaxInfo.Expression.builder(ExprSecDamageFunction.class, DamageFunctionWrapper.class) + .addPatterns("a [custom] [item] damage function") + .supplier(ExprSecDamageFunction::new) + .build() + ); + EventValues.registerEventValue(DamageFunctionSectionEvent.class, DamageFunctionWrapper.class, DamageFunctionSectionEvent::getWrapper); + } + + private Trigger trigger; + + @Override + public boolean init(Expression[] exprs, int pattern, Kleenean delayed, ParseResult result, @Nullable SectionNode node, @Nullable List triggerItems) { + if (node != null) { + AtomicBoolean isDelayed = new AtomicBoolean(false); + trigger = SectionUtils.loadLinkedCode("custom item damage function", (beforeLoading, afterLoading) -> + loadCode(node, "custom item damage function", beforeLoading, afterLoading, DamageFunctionSectionEvent.class) + ); + return trigger != null; + } + return true; + } + + @Override + protected DamageFunctionWrapper @Nullable [] get(Event event) { + DamageFunctionWrapper wrapper = new DamageFunctionWrapper(); + if (trigger != null) { + DamageFunctionSectionEvent sectionEvent = new DamageFunctionSectionEvent(wrapper); + Variables.withLocalVariables(event, sectionEvent, () -> TriggerItem.walk(trigger, sectionEvent)); + } + return new DamageFunctionWrapper[] {wrapper}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return DamageFunctionWrapper.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "a custom item damage function"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecReduction.java b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecReduction.java new file mode 100644 index 00000000000..18939d9c1a7 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/itemcomponents/blocking/elements/ExprSecReduction.java @@ -0,0 +1,120 @@ +package org.skriptlang.skript.bukkit.itemcomponents.blocking.elements; + +import ch.njol.skript.config.SectionNode; +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.SectionExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Trigger; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.SectionUtils; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.variables.Variables; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.BlockingExperimentalSyntax; +import org.skriptlang.skript.bukkit.itemcomponents.blocking.DamageReductionWrapper; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +@Name("Custom Damage Reduction") +@Description(""" + Gets a custom damage reduction. + Damage Reductions contain data that attribute to: + - What damage types can be being blocked + - The base amount of damage to block when blocking one of the damage types + - The factor amount of damage to block when blocking one of the damage types + - The angle at which the item can block when blocking one of the damage types + NOTE: Blocking component elements are experimental. Thus, they are subject to change and may not work as intended. + """) +@Example(""" + set {_reduction} to a custom damage reduction: + set the reduction angle to 90 + set the reduction base to 40 + set the reduction factor to 1 + set the reduction damage types to magic and explosion + add {_reduction} to the damage reductions of {_item} + """) +@RequiredPlugins("Minecraft 1.21.5+") +@Since("INSERT VERSION") +public class ExprSecReduction extends SectionExpression implements BlockingExperimentalSyntax { + + private static class BlankReductionSectionEvent extends Event { + + private final DamageReductionWrapper wrapper; + + public BlankReductionSectionEvent(DamageReductionWrapper wrapper) { + this.wrapper = wrapper; + } + + public DamageReductionWrapper getWrapper() { + return wrapper; + } + + @Override + public @NotNull HandlerList getHandlers() { + throw new IllegalStateException(); + } + } + + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + SyntaxInfo.Expression.builder(ExprSecReduction.class, DamageReductionWrapper.class) + .addPatterns("a [custom] damage reduction") + .supplier(ExprSecReduction::new) + .build() + ); + EventValues.registerEventValue(BlankReductionSectionEvent.class, DamageReductionWrapper.class, BlankReductionSectionEvent::getWrapper); + } + + private Trigger trigger; + + @Override + public boolean init(Expression[] exprs, int pattern, Kleenean delayed, ParseResult result, @Nullable SectionNode node, @Nullable List triggerItems) { + if (node != null) { + AtomicBoolean isDelayed = new AtomicBoolean(false); + trigger = SectionUtils.loadLinkedCode("custom damage reduction", (beforeLoading, afterLoading) -> + loadCode(node, "custom damage reduction", beforeLoading, afterLoading, BlankReductionSectionEvent.class) + ); + return trigger != null; + } + return true; + } + + @Override + protected DamageReductionWrapper @Nullable [] get(Event event) { + DamageReductionWrapper wrapper = new DamageReductionWrapper(); + if (trigger != null) { + BlankReductionSectionEvent sectionEvent = new BlankReductionSectionEvent(wrapper); + Variables.withLocalVariables(event, sectionEvent, () -> TriggerItem.walk(trigger, sectionEvent)); + } + return new DamageReductionWrapper[] {wrapper}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return DamageReductionWrapper.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "a custom damage reduction"; + } + +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index f131ca399b2..686a3b942cd 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -3122,6 +3122,9 @@ types: frogvariant: frog variant¦s @a itemcomponent: item component¦s @an equippablecomponent: equippable component¦s @an + blockingcomponent: blocking component¦s @a + damagereduction: damage reduction¦s @a + itemdamagefunction: item damage function¦s @an # Skript weathertype: weather type¦s @a diff --git a/src/test/skript/tests/general/BlockingComponents.sk b/src/test/skript/tests/general/BlockingComponents.sk new file mode 100644 index 00000000000..948408ba559 --- /dev/null +++ b/src/test/skript/tests/general/BlockingComponents.sk @@ -0,0 +1,145 @@ + +options: + blockSound: "minecraft:ui.toast.in" + disableSound: "minecraft:ui.toast.out" + damageFunction: a custom item damage function + delay: 5 seconds + disableScale: 10 + reduction: a custom damage reduction + + +using blocking components + +test "blocking component - new" when running minecraft "1.21.5": + set {_component} to a blank blocking component + set the blocked sound of {_component} to {@blockSound} + assert the blocked sound of {_component} is {@blockSound} with "Block sound of component was not set" + set the disabled sound of {_component} to {@disableSound} + assert the disabled sound of {_component} is {@disableSound} with "Disable sound of component was not set" + set the damage function of {_component} to {@damageFunction} + assert the damage function of {_component} is {@damageFunction} with "Damage function of component was not set" + set the blocking delay time of {_component} to {@delay} + assert the blocking delay time of {_component} is {@delay} with "Delay time of component was not set" + set the disable cooldown scale of {_component} to {@disableScale} + assert the disable cooldown scale of {_component} is {@disableScale} with "Disable cooldown scale of component was not set" + add {@reduction} to the damage reductions of {_component} + assert the damage reductions of {_component} contains {@reduction} with "Damage reduction was not added to component" + +test "blocking component - itemtype" when running minecraft "1.21.5": + set {_item} to a leather helmet (item type) + set the blocked sound of {_item} to {@blockSound} + assert the blocked sound of {_item} is {@blockSound} with "Block sound of itemtype was not set" + set the disabled sound of {_item} to {@disableSound} + assert the disabled sound of {_item} is {@disableSound} with "Disable sound of itemtype was not set" + set the damage function of {_item} to {@damageFunction} + assert the damage function of {_item} is {@damageFunction} with "Damage function of itemtype was not set" + set the blocking delay time of {_item} to {@delay} + assert the blocking delay time of {_item} is {@delay} with "Delay time of itemtype was not set" + set the disable cooldown scale of {_item} to {@disableScale} + assert the disable cooldown scale of {_item} is {@disableScale} with "Disable cooldown scale of itemtype was not set" + add {@reduction} to the damage reductions of {_item} + assert the damage reductions of {_item} contains {@reduction} with "Damage reduction was not added to itemtype" + +test "blocking component - itemstack" when running minecraft "1.21.5": + set {_item} to an iron helmet (item stack) + set the blocked sound of {_item} to {@blockSound} + assert the blocked sound of {_item} is {@blockSound} with "Block sound of itemstack was not set" + set the disabled sound of {_item} to {@disableSound} + assert the disabled sound of {_item} is {@disableSound} with "Disable sound of itemstack was not set" + set the damage function of {_item} to {@damageFunction} + assert the damage function of {_item} is {@damageFunction} with "Damage function of itemstack was not set" + set the blocking delay time of {_item} to {@delay} + assert the blocking delay time of {_item} is {@delay} with "Delay time of itemstack was not set" + set the disable cooldown scale of {_item} to {@disableScale} + assert the disable cooldown scale of {_item} is {@disableScale} with "Disable cooldown scale of itemstack was not set" + add {@reduction} to the damage reductions of {_item} + assert the damage reductions of {_item} contains {@reduction} with "Damage reduction was not added to itemstack" + +test "blocking component - slot" when running minecraft "1.21.5": + set {_gui} to a chest inventory with 1 row + set slot 1 of {_gui} to a diamond helmet + + set the blocked sound of (slot 1 of {_gui}) to {@blockSound} + assert the blocked sound of (slot 1 of {_gui}) is {@blockSound} with "Block sound of slot was not set" + set the disabled sound of (slot 1 of {_gui}) to {@disableSound} + assert the disabled sound of (slot 1 of {_gui}) is {@disableSound} with "Disable sound of slot was not set" + set the damage function of (slot 1 of {_gui}) to {@damageFunction} + assert the damage function of (slot 1 of {_gui}) is {@damageFunction} with "Damage function of slot was not set" + set the blocking delay time of (slot 1 of {_gui}) to {@delay} + assert the blocking delay time of (slot 1 of {_gui}) is {@delay} with "Delay time of slot was not set" + set the disable cooldown scale of (slot 1 of {_gui}) to {@disableScale} + assert the disable cooldown scale of (slot 1 of {_gui}) is {@disableScale} with "Disable cooldown scale of slot was not set" + add {@reduction} to the damage reductions of (slot 1 of {_gui}) + assert the damage reductions of (slot 1 of {_gui}) contains {@reduction} with "Damage reduction was not added to slot" + +test "blocking component - copy" when running minecraft "1.21.5": + set {_component} to a blank blocking component + set the blocked sound of {_component} to {@blockSound} + assert the blocked sound of {_component} is {@blockSound} with "Block sound of component was not set" + set the disabled sound of {_component} to {@disableSound} + assert the disabled sound of {_component} is {@disableSound} with "Disable sound of component was not set" + set the damage function of {_component} to {@damageFunction} + assert the damage function of {_component} is {@damageFunction} with "Damage function of component was not set" + set the blocking delay time of {_component} to {@delay} + assert the blocking delay time of {_component} is {@delay} with "Delay time of component was not set" + set the disable cooldown scale of {_component} to {@disableScale} + assert the disable cooldown scale of {_component} is {@disableScale} with "Disable cooldown scale of component was not set" + add {@reduction} to the damage reductions of {_component} + assert the damage reductions of {_component} contains {@reduction} with "Damage reduction was not added to component" + + set {_original} to {_component} + set {_copy} to item component copy of {_original} + set {_damage} to a custom item damage function: + set the damage function base of event-item damage function to 10 + + set the blocked sound of {_copy} to "minecraft:ui.toast.challenge_complete" + assert the blocked sound of {_copy} is "minecraft:ui.toast.challenge_complete" with "Block sound of copy was not set" + assert the blocked sound of {_original} is {@blockSound} with "Block sound of original should not have changed" + set the disabled sound of {_copy} to "minecraft:ui.toast.challenge_complete" + assert the disabled sound of {_copy} is "minecraft:ui.toast.challenge_complete" with "Disable sound of copy was not set" + assert the disabled sound of {_original} is {@disableSound} with "Disable sound of original should not have changed" + set the damage function of {_copy} to {_damage} + assert the damage function of {_copy} is {_damage} with "Damage function of copy was not set" + assert the damage function of {_original} is {@damageFunction} with "Damage function of original should not have changed" + set the blocking delay time of {_copy} to 10 seconds + assert the blocking delay time of {_copy} is 10 seconds with "Delay time of copy was not set" + assert the blocking delay time of {_original} is {@delay} with "Delay time of original should not have changed" + set the disable cooldown scale of {_copy} to 20 + assert the disable cooldown scale of {_copy} is 20 with "Disable cooldown scale of copy was not set" + assert the disable cooldown scale of {_original} is {@disableScale} with "Disable cooldown scale of original should not have changed" + assert the damage reductions of {_copy} contains {@reduction} with "Damage reduction was not in copy" + clear the damage reductions of {_copy} + assert the damage reductions of {_copy} is not set with "Damage reductions of copy was not cleared" + assert the damage reductions of {_original} contains {@reduction} with "Damage reductions of original should not have changed" + +test "blocking component - item damage function" when running minecraft "1.21.5": + set {_component} to a blank blocking component + set {_damageFunction} to the item damage function of {_component} + set the damage function base of {_damageFunction} to 10 + assert the damage function base of {_damageFunction} is 10 with "Base amount of damage function was not set" + assert the damage function base of {_component} is 10 with "Base amount of damage function did not modify component" + set the damage function factor of {_damageFunction} to 20 + assert the damage function factor of {_damageFunction} is 20 with "Factor amount of damage function was not set" + assert the damage function factor of {_component} is 20 with "Factor amount of damage function did not modify component" + set the damage function threshold of {_damageFunction} to 30 + assert the damage function threshold of {_damageFunction} is 30 with "Threshold amount of damage function was not set" + assert the damage function threshold of {_component} is 30 with "Threshold amount of damage function did not modify component" + + assert the item damage function of {_component} is {_damageFunction} with "Component item damage function did not match" + +test "blocking component - damage reduction" when running minecraft "1.21.5": + set {_component} to a blank blocking component + add (a custom damage reduction) to the damage reductions of {_component} + set {_reduction} to the first element out of (damage reductions of {_component}) + assert {_reduction} is set with "Component should have a reduction" + + set the reduction base of {_reduction} to 10 + assert the reduction base of {_reduction} is 10 with "Base amount of damage reduction was not set" + set the reduction factor of {_reduction} to 20 + assert the reduction factor of {_reduction} is 20 with "Factor amount of damage reduction was not set" + set the reduction angle of {_reduction} to 90 + assert the reduction angle of {_reduction} is 90 with "Angle of damage reduction was not set" + set the reduction damage types of {_reduction} to magic and explosion + # assert the reduction damage types of {_reduction} is magic and explosion with "Damage types of damage reduction was not set" + + assert the damage reductions of {_component} contains {_reduction} with "Component did not retain changes made from damage reduction"