diff --git a/src/main/java/net/goldenstack/loot/LootFunction.java b/src/main/java/net/goldenstack/loot/LootFunction.java index e09ebe3..e144a84 100644 --- a/src/main/java/net/goldenstack/loot/LootFunction.java +++ b/src/main/java/net/goldenstack/loot/LootFunction.java @@ -75,7 +75,9 @@ public interface LootFunction { Template.entry("exploration_map", ExplorationMap.class, ExplorationMap.SERIALIZER), Template.entry("set_name", SetName.class, SetName.SERIALIZER), Template.entry("set_instrument", SetInstrument.class, SetInstrument.SERIALIZER), - Template.entry("set_attributes", SetAttributes.class, SetAttributes.SERIALIZER) + Template.entry("set_attributes", SetAttributes.class, SetAttributes.SERIALIZER), + Template.entry("set_writable_book_pages", SetWritableBookPages.class, SetWritableBookPages.SERIALIZER), + Template.entry("set_written_book_pages", SetWrittenBookPages.class, SetWrittenBookPages.SERIALIZER) ) ); @@ -1045,4 +1047,75 @@ public record AttributeDirective(@NotNull NamespaceID id, @NotNull Attribute att } } + record SetWritableBookPages(@NotNull List predicates, @NotNull List> pages, @NotNull ListOperation operation) implements LootFunction { + + private static final @NotNull BinaryTagSerializer> PREDICATES = Serial.lazy(() -> LootPredicate.SERIALIZER).list().optional(List.of()); + private static final @NotNull BinaryTagSerializer>> PAGES = FilteredText.STRING_NBT_TYPE.list(); + + public static final @NotNull BinaryTagSerializer SERIALIZER = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull SetWritableBookPages value) { + return ((CompoundBinaryTag) ListOperation.SERIALIZER.write(context, value.operation)) + .put("conditions", PREDICATES.write(context, value.predicates)) + .put("pages", PAGES.write(context, value.pages)); + } + + @Override + public @NotNull SetWritableBookPages read(@NotNull Context context, @NotNull BinaryTag raw) { + if (!(raw instanceof CompoundBinaryTag tag)) throw new IllegalArgumentException("Expected a compound tag"); + + return new SetWritableBookPages( + PREDICATES.read(context, tag.get("conditions")), + PAGES.read(context, tag.get("pages")), + ListOperation.SERIALIZER.read(context, tag) + ); + } + }; + + @Override + public @NotNull ItemStack apply(@NotNull ItemStack input, @NotNull LootContext context) { + if (!LootPredicate.all(predicates, context)) return input; + + WritableBookContent content = input.get(ItemComponent.WRITABLE_BOOK_CONTENT, WritableBookContent.EMPTY); + + return input.with(ItemComponent.WRITABLE_BOOK_CONTENT, new WritableBookContent(operation.apply(pages, content.pages()))); + } + } + + record SetWrittenBookPages(@NotNull List predicates, @NotNull List> pages, @NotNull ListOperation operation) implements LootFunction { + + private static final @NotNull BinaryTagSerializer> PREDICATES = Serial.lazy(() -> LootPredicate.SERIALIZER).list().optional(List.of()); + private static final @NotNull BinaryTagSerializer>> PAGES = FilteredText.COMPONENT_NBT_TYPE.list(); + + public static final @NotNull BinaryTagSerializer SERIALIZER = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull SetWrittenBookPages value) { + return ((CompoundBinaryTag) ListOperation.SERIALIZER.write(context, value.operation)) + .put("conditions", PREDICATES.write(context, value.predicates)) + .put("pages", PAGES.write(context, value.pages)); + } + + @Override + public @NotNull SetWrittenBookPages read(@NotNull Context context, @NotNull BinaryTag raw) { + if (!(raw instanceof CompoundBinaryTag tag)) throw new IllegalArgumentException("Expected a compound tag"); + + return new SetWrittenBookPages( + PREDICATES.read(context, tag.get("conditions")), + PAGES.read(context, tag.get("pages")), + ListOperation.SERIALIZER.read(context, tag) + ); + } + }; + + @Override + public @NotNull ItemStack apply(@NotNull ItemStack input, @NotNull LootContext context) { + if (!LootPredicate.all(predicates, context)) return input; + + WrittenBookContent content = input.get(ItemComponent.WRITTEN_BOOK_CONTENT, WrittenBookContent.EMPTY); + WrittenBookContent updated = new WrittenBookContent(operation.apply(pages, content.pages()), content.title(), content.author(), content.generation(), content.resolved()); + + return input.with(ItemComponent.WRITTEN_BOOK_CONTENT, updated); + } + } + } diff --git a/src/main/java/net/goldenstack/loot/util/ListOperation.java b/src/main/java/net/goldenstack/loot/util/ListOperation.java new file mode 100644 index 0000000..da0c95e --- /dev/null +++ b/src/main/java/net/goldenstack/loot/util/ListOperation.java @@ -0,0 +1,74 @@ +package net.goldenstack.loot.util; + +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public sealed interface ListOperation { + + @SuppressWarnings("UnstableApiUsage") + @NotNull BinaryTagSerializer SERIALIZER = Template.registry("mode", + Template.entry("append", Append.class, Template.template(Append::new)), + Template.entry("insert", Insert.class, Template.template( + "offset", BinaryTagSerializer.INT.optional(0), Insert::offset, + Insert::new + )), + Template.entry("replace_all", ReplaceAll.class, Template.template(ReplaceAll::new)), + Template.entry("replace_section", ReplaceSection.class, Template.template( + "offset", BinaryTagSerializer.INT.optional(0), ReplaceSection::offset, + "size", BinaryTagSerializer.INT.optional(), ReplaceSection::size, + ReplaceSection::new + )) + ); + + @NotNull List apply(@NotNull List values, @NotNull List input); + + record Append() implements ListOperation { + @Override + public @NotNull List apply(@NotNull List values, @NotNull List input) { + return Stream.concat(input.stream(), values.stream()).toList(); + + } + } + + record Insert(int offset) implements ListOperation { + @Override + public @NotNull List apply(@NotNull List values, @NotNull List input) { + List items = new ArrayList<>(); + items.addAll(input.subList(0, this.offset)); + items.addAll(values); + items.addAll(input.subList(this.offset, input.size())); + return items; + } + } + + record ReplaceAll() implements ListOperation { + @Override + public @NotNull List apply(@NotNull List values, @NotNull List input) { + return values; + } + } + + record ReplaceSection(int offset, @Nullable Integer size) implements ListOperation { + @Override + public @NotNull List apply(@NotNull List values, @NotNull List input) { + List items = new ArrayList<>(); + items.addAll(input.subList(0, offset)); + items.addAll(values); + + int size = this.size != null ? this.size : values.size(); + + // Add truncated part of list of possible + if (offset + size < input.size()) { + items.addAll(input.subList(offset + size, input.size())); + } + + return items; + } + } + +}