diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java index 1583d40833..984f793a42 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java @@ -60,6 +60,27 @@ public interface PotionContents { @Contract(pure = true) @Nullable String customName(); + /** + * All effects that this component applies. + *

+ * This is a combination of the base potion type and any custom effects. + * + * @return an unmodifiable list of all effects. + */ + @Contract(pure = true) + @Unmodifiable List allEffects(); + + /** + * Computes the effective colour of this potion contents component. + *

+ * This blends all custom effects, or uses a default fallback colour. + * It may or may not have an alpha channel, used for tipped arrows. + * + * @return the effective colour this component would display with. + */ + @Contract(pure = true) + Color computeEffectiveColor(); + @ApiStatus.Experimental @ApiStatus.NonExtendable interface Builder extends DataComponentBuilder { diff --git a/paper-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java b/paper-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java index 02b0a3878b..3364254ada 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java +++ b/paper-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java @@ -8,6 +8,7 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; /** * Represents a potion or item that can have custom effects. @@ -74,6 +75,16 @@ public interface PotionMeta extends ItemMeta { @NotNull List getCustomEffects(); + /** + * All effects that this potion meta holds. + *

+ * This is a combination of the base potion type and any custom effects. + * + * @return an unmodifiable list of all effects. + */ + @NotNull + @Unmodifiable List getAllEffects(); + /** * Adds a custom potion effect to this potion. * @@ -146,6 +157,16 @@ public interface PotionMeta extends ItemMeta { */ void setColor(@Nullable Color color); + /** + * Computes the effective colour of this potion meta. + *

+ * This blends all custom effects, or uses a default fallback color. + * + * @return the effective potion color + */ + @NotNull + Color computeEffectiveColor(); + /** * Checks for existence of a custom potion name translation suffix. * diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java index 4712f8bbaa..d1ddcc17db 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java @@ -5,6 +5,8 @@ import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import net.minecraft.world.effect.MobEffectInstance; import org.bukkit.Color; import org.bukkit.craftbukkit.potion.CraftPotionType; @@ -48,6 +50,19 @@ public record PaperPotionContents( return this.impl.customName().orElse(null); } + @Override + public @Unmodifiable List allEffects() { + //noinspection SimplifyStreamApiCallChains - explicity want it unmodifiable, as toList() api doesnt guarantee this. + return StreamSupport.stream(this.impl.getAllEffects().spliterator(), false) + .map(CraftPotionUtil::toBukkit) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public Color computeEffectiveColor() { + return Color.fromARGB(this.impl.getColor()); + } + static final class BuilderImpl implements PotionContents.Builder { private final List customEffects = new ObjectArrayList<>(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java index a3d3ea247a..05db0af608 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java @@ -1,6 +1,7 @@ package org.bukkit.craftbukkit.inventory; import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap.Builder; import java.util.ArrayList; @@ -25,6 +26,7 @@ import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; +import org.jetbrains.annotations.NotNull; @DelegateDeserialization(SerializableMeta.class) class CraftMetaPotion extends CraftMetaItem implements PotionMeta { @@ -202,6 +204,19 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { return ImmutableList.of(); } + @Override + @NotNull + public List getAllEffects() { + final ImmutableList.Builder builder = ImmutableList.builder(); + if (this.hasBasePotionType()) { + builder.addAll(this.getBasePotionType().getPotionEffects()); + } + if (this.hasCustomEffects()) { + builder.addAll(this.customEffects); + } + return builder.build(); + } + @Override public boolean addCustomEffect(PotionEffect effect, boolean overwrite) { Preconditions.checkArgument(effect != null, "Potion effect cannot be null"); @@ -305,6 +320,17 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { this.color = color == null ? null : color.asRGB(); } + @Override + @NotNull + public Color computeEffectiveColor() { + if (hasColor()) return getColor(); + + return Color.fromRGB( + PotionContents.getColorOptional(Collections2.transform(getAllEffects(), CraftPotionUtil::fromBukkit)) + .orElse(PotionContents.BASE_POTION_COLOR) & 0xFFFFFF + ); + } + @Override public boolean hasCustomPotionName() { return this.customName != null;