diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index a8bdcaad4b7..9382a0cd0ee 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -211,6 +211,7 @@ public void setTo(ItemType i) { for (final ItemData d : i) { types.add(d.clone()); } + isRandom = i.isRandom; } public ItemType(Block block) { @@ -669,6 +670,8 @@ public ItemType clone() { private final static Random random = new Random(); + public boolean isRandom = false; + /** * @return One random ItemStack that this ItemType represents. If you have a List or an Inventory, use {@link #addTo(Inventory)} or {@link #addTo(List)} respectively. * @see #addTo(Inventory) @@ -680,6 +683,11 @@ public ItemType clone() { * @see #removeFrom(List...) */ public @Nullable ItemStack getRandom() { + if (isRandom) { + ItemStack stack = types.getFirst().getStack(); + stack.setAmount(getAmount()); + return stack; + } List datas = types.stream() .filter(data -> data.stack != null) .collect(Collectors.toList()); diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 8f615650611..528a7d99db4 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -62,7 +62,7 @@ import org.bukkit.inventory.*; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; -import org.skriptlang.skript.bukkit.item.book.elements.expressions.ExprBookPages; +import org.skriptlang.skript.bukkit.item.book.BookUtils; import org.skriptlang.skript.bukkit.lang.eventvalue.EventValue; import org.skriptlang.skript.bukkit.lang.eventvalue.EventValue.Time; import org.skriptlang.skript.bukkit.lang.eventvalue.EventValueRegistry; @@ -577,11 +577,11 @@ else if (hand == EquipmentSlot.OFF_HAND) return book; })); registry.register(EventValue.builder(PlayerEditBookEvent.class, Component[].class) - .getter(event -> ExprBookPages.getPages(event.getPreviousBookMeta()).toArray(new Component[0])) + .getter(event -> BookUtils.getPages(event.getPreviousBookMeta()).toArray(new Component[0])) .time(Time.PAST) .build()); registry.register(EventValue.simple(PlayerEditBookEvent.class, Component[].class, event -> - ExprBookPages.getPages(event.getNewBookMeta()).toArray(new Component[0]))); + BookUtils.getPages(event.getNewBookMeta()).toArray(new Component[0]))); //ItemDespawnEvent registry.register(EventValue.simple(ItemDespawnEvent.class, Item.class, ItemDespawnEvent::getEntity)); registry.register(EventValue.simple(ItemDespawnEvent.class, ItemStack.class, event -> event.getEntity().getItemStack())); diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java index f87f7eb49a0..66c8ab0e0b2 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java @@ -131,6 +131,11 @@ public DefaultConverters() {} ItemStack i = s.getItem(); return new ItemType(i != null ? i : new ItemStack(Material.AIR, 1)); }); + // Slot - ItemStack + Converters.registerConverter(Slot.class, ItemStack.class, slot -> { + ItemStack item = slot.getItem(); + return item == null ? new ItemStack(Material.AIR, 1) : item; + }); // Block - InventoryHolder Converters.registerConverter(Block.class, InventoryHolder.class, b -> { diff --git a/src/main/java/ch/njol/skript/effects/EffOpenBook.java b/src/main/java/ch/njol/skript/effects/EffOpenBook.java deleted file mode 100644 index c9e4062b103..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffOpenBook.java +++ /dev/null @@ -1,66 +0,0 @@ -package ch.njol.skript.effects; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.Aliases; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Example; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.RequiredPlugins; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; - -@Name("Open Book") -@Description("Opens a written book to a player.") -@Example("open book player's tool to player") -@RequiredPlugins("Minecraft 1.14.2+") -@Since("2.5.1") -public class EffOpenBook extends Effect { - - static { - if (Skript.methodExists(Player.class, "openBook", ItemStack.class)) { - Skript.registerEffect(EffOpenBook.class, "(open|show) book %itemtype% (to|for) %players%"); - } - } - - @SuppressWarnings("null") - private Expression book; - @SuppressWarnings("null") - private Expression players; - - @SuppressWarnings({"unchecked", "null"}) - @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - book = (Expression) exprs[0]; - players = (Expression) exprs[1]; - return true; - } - - @Override - protected void execute(final Event e) { - ItemType itemType = book.getSingle(e); - if (itemType != null) { - ItemStack itemStack = itemType.getRandom(); - if (itemStack != null && itemStack.getType() == Material.WRITTEN_BOOK) { - for (Player player : players.getArray(e)) { - player.openBook(itemStack); - } - } - } - } - - @Override - public String toString(@Nullable Event e, boolean debug) { - return "open book " + book.toString(e, debug) + " to " + players.toString(e, debug); - } - -} diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/book/BookModule.java b/src/main/java/org/skriptlang/skript/bukkit/item/book/BookModule.java index b74e1396d93..6da921678a8 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/item/book/BookModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/item/book/BookModule.java @@ -3,6 +3,7 @@ import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.HierarchicalAddonModule; import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.item.book.elements.effects.*; import org.skriptlang.skript.bukkit.item.book.elements.expressions.*; public class BookModule extends HierarchicalAddonModule { @@ -14,6 +15,7 @@ public BookModule(AddonModule parentModule) { @Override protected void loadSelf(SkriptAddon addon) { register(addon, + EffOpenBook::register, ExprBookAuthor::register, ExprBookPages::register, ExprBookTitle::register diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/book/BookUtils.java b/src/main/java/org/skriptlang/skript/bukkit/item/book/BookUtils.java new file mode 100644 index 00000000000..73ca4df8921 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/book/BookUtils.java @@ -0,0 +1,102 @@ +package org.skriptlang.skript.bukkit.item.book; + +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.WritableBookContent; +import io.papermc.paper.datacomponent.item.WrittenBookContent; +import io.papermc.paper.text.Filtered; +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.function.Consumer; + +@SuppressWarnings("UnstableApiUsage") +public final class BookUtils { + + private BookUtils() { } + + public static WrittenBookContent modifyWrittenContent(WrittenBookContent content, Consumer modifier) { + return modifyWrittenContent(content, modifier, true); + } + + public static WrittenBookContent modifyWrittenContent(WrittenBookContent content, Consumer modifier, boolean preservePages) { + WrittenBookContent.Builder builder = WrittenBookContent.writtenBookContent(content.title(), content.author()) + .generation(content.generation()) + .resolved(content.resolved()); + if (preservePages) { + //noinspection unchecked, rawtypes + builder.addFilteredPages((List) content.pages()); + } + modifier.accept(builder); + return builder.build(); + } + + public static List getPages(ItemStack book) { + if (book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT)) { + //noinspection ConstantConditions - checked via hasData + return book.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT).pages().stream() + .map(Filtered::raw) + .toList(); + } + if (book.hasData(DataComponentTypes.WRITABLE_BOOK_CONTENT)) { + //noinspection ConstantConditions - checked via hasData + return book.getData(DataComponentTypes.WRITABLE_BOOK_CONTENT).pages().stream() + .map(filtered -> Component.text(filtered.raw())) + .toList(); + } + return List.of(); + } + + public static void setPages(ItemStack book, List pages) { + boolean hasWrittenBookContent = book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT); + if (hasWrittenBookContent || book.getType() == Material.WRITTEN_BOOK) { + WrittenBookContent newContent; + if (hasWrittenBookContent) { + //noinspection ConstantConditions - checked via hasData + newContent = modifyWrittenContent(book.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT), + content -> content.addPages(pages), false); + } else { + newContent = WrittenBookContent.writtenBookContent(getDefaultTitle(book), getDefaultAuthor(book)) + .addPages(pages) + .build(); + } + book.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, newContent); + return; + } + + if (book.hasData(DataComponentTypes.WRITABLE_BOOK_CONTENT) || book.getType() == Material.WRITABLE_BOOK) { + LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection(); + List stringPages = pages.stream() + .map(serializer::serialize) + .toList(); + book.setData(DataComponentTypes.WRITABLE_BOOK_CONTENT, WritableBookContent.writeableBookContent() + .addPages(stringPages) + .build()); + } + } + + public static String getDefaultTitle(ItemStack book) { + return LegacyComponentSerializer.legacySection().serialize(book.effectiveName()); + } + + public static String getDefaultAuthor(ItemStack book) { + return "Unknown"; + } + + @SuppressWarnings("ConstantValue") // true on 26.1 and older + private static final boolean EXTENDS_ADVENTURE_BOOK = Book.class.isAssignableFrom(BookMeta.class); + + public static @Unmodifiable List getPages(BookMeta bookMeta) { + if (EXTENDS_ADVENTURE_BOOK) { + //noinspection ConstantConditions + return ((Book) (Object) bookMeta).pages(); + } + return bookMeta.pages(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/effects/EffOpenBook.java b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/effects/EffOpenBook.java new file mode 100644 index 00000000000..4f0091de34d --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/effects/EffOpenBook.java @@ -0,0 +1,62 @@ +package org.skriptlang.skript.bukkit.item.book.elements.effects; + +import ch.njol.skript.lang.SyntaxStringBuilder; +import io.papermc.paper.datacomponent.DataComponentTypes; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Open Book") +@Description("Opens a written book to a player.") +@Example("open book player's tool to player") +@Since("2.5.1") +public class EffOpenBook extends Effect { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register(SyntaxRegistry.EFFECT, SyntaxInfo.simple(EffOpenBook.class, EffOpenBook::new, + "(open|show) book %itemstack% (to|for) %players%")); + } + + private Expression book; + private Expression players; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + book = (Expression) expressions[0]; + //noinspection unchecked + players = (Expression) expressions[1]; + return true; + } + + @Override + protected void execute(Event event) { + ItemStack book = this.book.getSingle(event); + if (book == null || !book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT)) { + return; + } + for (Player player : players.getArray(event)) { + player.openBook(book); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return new SyntaxStringBuilder(event, debug) + .append("open book", book, "to", players) + .toString(); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookAuthor.java b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookAuthor.java index b4aa3823492..7db60fc1577 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookAuthor.java +++ b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookAuthor.java @@ -1,6 +1,5 @@ package org.skriptlang.skript.bukkit.item.book.elements.expressions; -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; @@ -8,10 +7,15 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.WrittenBookContent; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Material; import org.bukkit.event.Event; -import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.item.book.BookUtils; import org.skriptlang.skript.registration.SyntaxRegistry; @Name("Book Author") @@ -21,19 +25,21 @@ broadcast "A new book has been created by %author of event-item%" """) @Since("2.2-dev31") -public class ExprBookAuthor extends SimplePropertyExpression { +public class ExprBookAuthor extends SimplePropertyExpression { public static void register(SyntaxRegistry syntaxRegistry) { syntaxRegistry.register(SyntaxRegistry.EXPRESSION, infoBuilder(ExprBookAuthor.class, Component.class, - "[book] (author|writer|publisher)", "itemtypes", false) + "[book] (author|writer|publisher)", "itemstacks", false) .supplier(ExprBookAuthor::new) .build()); } @Override - public @Nullable Component convert(ItemType item) { - if (item.getItemMeta() instanceof BookMeta bookMeta && bookMeta.hasAuthor()) { - return bookMeta.author(); + public @Nullable Component convert(ItemStack book) { + if (book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT)) { + //noinspection ConstantConditions - checked via hasData + return LegacyComponentSerializer.legacySection() + .deserialize(book.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT).author()); } return null; } @@ -48,12 +54,20 @@ public static void register(SyntaxRegistry syntaxRegistry) { @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - Component author = delta == null ? null : (Component) delta[0]; - for (ItemType item : getExpr().getArray(event)) { - if (item.getItemMeta() instanceof BookMeta bookMeta) { - bookMeta.author(author); - item.setItemMeta(bookMeta); + String author = delta == null ? "" : LegacyComponentSerializer.legacySection().serialize(((Component) delta[0])); + for (ItemStack book : getExpr().getArray(event)) { + boolean hasContent = book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT); + if (!hasContent && book.getType() != Material.WRITTEN_BOOK) { + continue; } + WrittenBookContent newContent; + if (hasContent) { + newContent = BookUtils.modifyWrittenContent(book.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT), + content -> content.author(author)); + } else { + newContent = WrittenBookContent.writtenBookContent(BookUtils.getDefaultTitle(book), author).build(); + } + book.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, newContent); } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookPages.java b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookPages.java index dd73850b10c..546a71c0be8 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookPages.java +++ b/src/main/java/org/skriptlang/skript/bukkit/item/book/elements/expressions/ExprBookPages.java @@ -1,6 +1,5 @@ package org.skriptlang.skript.bukkit.item.book.elements.expressions; -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; @@ -13,26 +12,24 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; -import net.kyori.adventure.inventory.Book; import net.kyori.adventure.text.Component; -import org.bukkit.Material; import org.bukkit.event.Event; -import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.bukkit.item.book.BookUtils; import org.skriptlang.skript.registration.SyntaxInfo; import org.skriptlang.skript.registration.SyntaxRegistry; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; @Name("Book Pages") -@Description({ - "The pages of a book (Supports Skript's chat format)", - "Note: In order to modify the pages of a new written book, you must have the title and author", - "of the book set. Skript will do this for you, but if you want your own, please set those values." -}) +@Description(""" + The pages of a written or writable book. + Note: In order to modify the pages of a new written book, the book must have a title and author set. \ + Skript will handle this manually, but if you want those to be something else, you must set them. + """) @Example(""" on book sign: if the number of pages of event-item is greater than 1: @@ -42,50 +39,30 @@ @Since("2.2-dev31, 2.7 (changers)") public class ExprBookPages extends SimpleExpression { - @SuppressWarnings("ConstantValue") // true on 26.1 and older - private static final boolean EXTENDS_ADVENTURE_BOOK = Book.class.isAssignableFrom(BookMeta.class); - - public static @Unmodifiable List getPages(BookMeta bookMeta) { - if (EXTENDS_ADVENTURE_BOOK) { - //noinspection ConstantConditions - return ((Book) (Object) bookMeta).pages(); - } - return bookMeta.pages(); - } - - public static void setPages(BookMeta bookMeta, List pages) { - if (EXTENDS_ADVENTURE_BOOK) { - //noinspection ConstantConditions, ResultOfMethodCallIgnored - modifies in place despite contract - ((Book) (Object) bookMeta).pages(pages); - } else { - bookMeta.pages(pages); - } - } - public static void register(SyntaxRegistry syntaxRegistry) { syntaxRegistry.register(SyntaxRegistry.EXPRESSION, SyntaxInfo.Expression.builder(ExprBookPages.class, Component.class) .supplier(ExprBookPages::new) .priority(PropertyExpression.DEFAULT_PRIORITY) - .addPatterns("[all [[of] the]|the] [book] (pages|content) of %itemtypes%", - "%itemtypes%'[s] [book] (pages|content)", - "[book] page %integer% of %itemtypes%", - "%itemtypes%'[s] [book] page %integer%") + .addPatterns("[all [[of] the]|the] [book] (pages|content) of %itemstacks%", + "%itemstacks%'[s] [book] (pages|content)", + "[book] page %integer% of %itemstacks%", + "%itemstacks%'[s] [book] page %integer%") .build()); } - private Expression books; + private Expression books; private @Nullable Expression pageNumber; @Override @SuppressWarnings("unchecked") public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { if (matchedPattern == 0 || matchedPattern == 1) { - books = (Expression) expressions[0]; + books = (Expression) expressions[0]; } else if (matchedPattern == 2) { pageNumber = (Expression) expressions[0]; - books = (Expression) expressions[1]; + books = (Expression) expressions[1]; } else { - books = (Expression) expressions[0]; + books = (Expression) expressions[0]; pageNumber = (Expression) expressions[1]; } return true; @@ -93,25 +70,23 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override protected Component[] get(Event event) { - List pages = new ArrayList<>(); - for (ItemType book : books.getArray(event)) { - if (book.getMaterial() != Material.WRITTEN_BOOK || !(book.getItemMeta() instanceof BookMeta bookMeta)) { - return new Component[0]; - } + List allPages = new ArrayList<>(); + for (ItemStack book : books.getArray(event)) { + List pages = BookUtils.getPages(book); if (isAllPages()) { - pages.addAll(getPages(bookMeta)); - } else { - Integer pageNumber = this.pageNumber.getSingle(event); - if (pageNumber == null) { - continue; - } - if (pageNumber <= 0 || pageNumber > bookMeta.getPageCount()) { - continue; - } - pages.add(bookMeta.page(pageNumber)); + allPages.addAll(pages); + continue; + } + Integer pageNumber = this.pageNumber.getSingle(event); + if (pageNumber == null) { + continue; } + if (pageNumber <= 0 || pageNumber > pages.size()) { + continue; + } + allPages.add(pages.get(pageNumber - 1)); } - return pages.toArray(new Component[0]); + return allPages.toArray(new Component[0]); } @Override @@ -140,56 +115,42 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { } } - for (ItemType book : books.getArray(event)) { - if (book.getMaterial() != Material.WRITTEN_BOOK || !(book.getItemMeta() instanceof BookMeta bookMeta)) { - continue; - } - + for (ItemStack book : books.getArray(event)) { if (isAllPages()) { switch (mode) { - case SET, DELETE, RESET -> setPages(bookMeta, newPages); - case ADD -> bookMeta.addPages(newPages.toArray(new Component[0])); + case SET, DELETE, RESET -> BookUtils.setPages(book, newPages); + case ADD -> { + List pages = new ArrayList<>(BookUtils.getPages(book)); + pages.addAll(newPages); + BookUtils.setPages(book, pages); + } default -> throw new IllegalStateException(); } } else { + List pages = new ArrayList<>(BookUtils.getPages(book)); switch (mode) { case SET -> { - if (pageNumber > bookMeta.getPageCount()) { - Component[] pages = new Component[pageNumber - bookMeta.getPageCount()]; - Arrays.fill(pages, Component.empty()); - bookMeta.addPages(pages); + if (pageNumber > pages.size()) { + pages.addAll(Collections.nCopies(pageNumber - pages.size(), Component.empty())); } - bookMeta.page(pageNumber, newPages.getFirst()); + pages.set(pageNumber - 1, newPages.getFirst()); } case DELETE -> { - if (pageNumber > bookMeta.getPageCount()) { + if (pageNumber > pages.size()) { break; } - List pages = new ArrayList<>(getPages(bookMeta)); pages.remove(pageNumber - 1); - setPages(bookMeta, pages); } case RESET -> { - if (pageNumber > bookMeta.getPageCount()) { + if (pageNumber > pages.size()) { continue; } - bookMeta.page(pageNumber, Component.empty()); + pages.set(pageNumber - 1, Component.empty()); } default -> throw new IllegalStateException(); } + BookUtils.setPages(book, pages); } - - // if the title and author of the bookMeta are not set, Minecraft will not update the BookMeta, as it deems the book "not signed". - if (!bookMeta.hasTitle()) { - Component title = bookMeta.hasDisplayName() ? bookMeta.displayName() : Component.text("Written Book"); - bookMeta.title(title); - } - if (!bookMeta.hasAuthor()) { - bookMeta.author(Component.text("Unknown")); - } - - // update book - book.setItemMeta(bookMeta); } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/types/ItemStackClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/types/ItemStackClassInfo.java index a68794d1c06..5bf55bfcebc 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/types/ItemStackClassInfo.java +++ b/src/main/java/org/skriptlang/skript/bukkit/types/ItemStackClassInfo.java @@ -5,17 +5,25 @@ import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.ItemUtils; import ch.njol.skript.classes.Changer; +import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.classes.ConfigurationSerializer; import ch.njol.skript.classes.Parser; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; +import ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.WrittenBookContent; +import io.papermc.paper.text.Filtered; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.item.book.BookUtils; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.handlers.base.ExpressionPropertyHandler; @@ -51,7 +59,11 @@ public ItemStackClassInfo() { .property(Property.AMOUNT, "The number of items in this stack. Can be set.", Skript.instance(), - new ItemStackAmountHandler()); + new ItemStackAmountHandler()) + .property(Property.TITLE, + "An item's title. This currently only applies to signed books.", + Skript.instance(), + new ItemTypeTitleHandler()); } private static class ItemStackParser extends Parser { @@ -126,4 +138,47 @@ public void change(ItemStack itemStack, Object @Nullable [] delta, Changer.Chang // } + private static class ItemTypeTitleHandler implements ExpressionPropertyHandler { + // + @Override + public @Nullable Component convert(ItemStack item) { + if (item.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT)) { + //noinspection ConstantConditions - checked via hasData + return LegacyComponentSerializer.legacySection() + .deserialize(item.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT).title().raw()); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, RESET -> CollectionUtils.array(Component.class); + default -> null; + }; + } + + @Override + public void change(ItemStack item, Object @Nullable [] delta, ChangeMode mode) { + boolean hasBookContent = item.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT); + if (hasBookContent || item.getType() == Material.WRITTEN_BOOK) { + String title = delta == null ? "" : LegacyComponentSerializer.legacySection().serialize((Component) delta[0]); + WrittenBookContent newContent; + if (hasBookContent) { + newContent = BookUtils.modifyWrittenContent(item.getData(DataComponentTypes.WRITTEN_BOOK_CONTENT), + content -> content.title(title)); + } else { + newContent = WrittenBookContent.writtenBookContent(Filtered.of(title, title), BookUtils.getDefaultAuthor(item)).build(); + } + item.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, newContent); + } + } + + @Override + public @NotNull Class returnType() { + return Component.class; + } + // + } + } diff --git a/src/main/java/org/skriptlang/skript/bukkit/types/ItemTypeClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/types/ItemTypeClassInfo.java index 0f97d964b88..4aaa121ea11 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/types/ItemTypeClassInfo.java +++ b/src/main/java/org/skriptlang/skript/bukkit/types/ItemTypeClassInfo.java @@ -10,12 +10,9 @@ import ch.njol.skript.classes.YggdrasilSerializer; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.util.EnchantmentType; -import ch.njol.util.coll.CollectionUtils; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.event.Event; -import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -50,10 +47,6 @@ public ItemTypeClassInfo() { .parser(new ItemTypeParser()) .cloner(ItemType::clone) .serializer(new YggdrasilSerializer<>()) - .property(Property.TITLE, - "An item's title. This currently only applies to signed books.", - Skript.instance(), - new ItemTypeTitleHandler()) .property(Property.NAME, "An item type's custom name, if set. Can be set or reset.", Skript.instance(), @@ -72,7 +65,11 @@ private static class ItemTypeParser extends Parser { // @Override public @Nullable ItemType parse(String s, ParseContext context) { - return Aliases.parseItemType(s); + ItemType itemType = Aliases.parseItemType(s); + if (itemType != null && itemType.getTypes().size() == 1 && itemType.getTypes().getFirst().getStack() != null) { + itemType.isRandom = true; + } + return itemType; } @Override @@ -110,40 +107,6 @@ public String toVariableNameString(ItemType itemType) { // } - private static class ItemTypeTitleHandler implements ExpressionPropertyHandler { - // - @Override - public @Nullable Component convert(ItemType item) { - if (item.getItemMeta() instanceof BookMeta bookMeta && bookMeta.hasTitle()) { - return bookMeta.title(); - } - return null; - } - - @Override - public Class @Nullable [] acceptChange(ChangeMode mode) { - return switch (mode) { - case SET, DELETE, RESET -> CollectionUtils.array(Component.class); - default -> null; - }; - } - - @Override - public void change(ItemType item, Object @Nullable [] delta, ChangeMode mode) { - Component title = delta == null ? null : (Component) delta[0]; - if (item.getItemMeta() instanceof BookMeta bookMeta) { - bookMeta.title(title); - item.setItemMeta(bookMeta); - } - } - - @Override - public @NotNull Class returnType() { - return Component.class; - } - // - } - private static class ItemTypeNameHandler implements ExpressionPropertyHandler { // @Override