diff --git a/purpur-server/minecraft-patches/features/0022-config-for-turning-bundles-into-functional-quivers.patch b/purpur-server/minecraft-patches/features/0022-config-for-turning-bundles-into-functional-quivers.patch new file mode 100644 index 000000000..c46e78c52 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0022-config-for-turning-bundles-into-functional-quivers.patch @@ -0,0 +1,207 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: granny +Date: Mon, 16 Oct 2023 21:54:47 -0700 +Subject: [PATCH] config for turning bundles into functional quivers + + +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 769acea46e91e07b711fbc6a92cfcad0564f5463..a99df447496225971a6be65a464e01f4f1cc6058 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -4824,6 +4824,11 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + } + + public ItemStack getProjectile(final ItemStack heldWeapon) { ++ // Purpur start - config for turning bundles into functional quivers ++ return getProjectile(heldWeapon, false, false); ++ } ++ public ItemStack getProjectile(ItemStack heldWeapon, boolean useBundleComponentStack, boolean useBundleItemStack) { ++ // Purpur end - config for turning bundles into functional quivers + return ItemStack.EMPTY; + } + +diff --git a/net/minecraft/world/entity/monster/Monster.java b/net/minecraft/world/entity/monster/Monster.java +index 81f1d4faebc38aa07a2c76c2c163584c8e5b624c..675b48168fd8c8ee06b67774440ff58d1c6b1b34 100644 +--- a/net/minecraft/world/entity/monster/Monster.java ++++ b/net/minecraft/world/entity/monster/Monster.java +@@ -154,6 +154,12 @@ public abstract class Monster extends PathfinderMob implements Enemy { + + @Override + public ItemStack getProjectile(final ItemStack heldWeapon) { ++ // Purpur start - config for turning bundles into functional quivers ++ return getProjectile(heldWeapon, false, false); ++ } ++ @Override ++ public ItemStack getProjectile(ItemStack heldWeapon, boolean useBundleComponentStack, boolean useBundleItemStack) { ++ // Purpur end - config for turning bundles into functional quivers + if (heldWeapon.getItem() instanceof ProjectileWeaponItem) { + Predicate supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getSupportedHeldProjectiles(); + ItemStack heldProjectile = ProjectileWeaponItem.getHeldProjectile(this, supportedProjectiles); +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 79f0bbfcf8ccdcbee99042d15adb5eb2338da124..0aec5c4b9c965a6f195aea4580c37c47123b0a50 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -2119,6 +2119,12 @@ public abstract class Player extends Avatar implements ContainerUser { + + @Override + public ItemStack getProjectile(final ItemStack heldWeapon) { ++ // Purpur start - config for turning bundles into functional quivers ++ return getProjectile(heldWeapon, false, false); ++ } ++ @Override ++ public ItemStack getProjectile(ItemStack heldWeapon, boolean useBundleComponentStack, boolean useBundleItemStack) { ++ // Purpur end - config for turning bundles into functional quivers + if (!(heldWeapon.getItem() instanceof ProjectileWeaponItem)) { + return ItemStack.EMPTY; + } +@@ -2131,9 +2137,22 @@ public abstract class Player extends Avatar implements ContainerUser { + } + + supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getAllSupportedProjectiles().and(item -> this.tryReadyArrow(heldWeapon, item, anyEventCancelled)); // Paper - PlayerReadyArrowEvent ++ // Purpur start - config for turning bundles into functional quivers ++ ItemStack bundleProjectiles; ++ if (useBundleComponentStack) { ++ bundleProjectiles = getProjectileFromBundle(heldWeapon, supportedProjectiles); ++ if (!bundleProjectiles.isEmpty()) return bundleProjectiles; ++ } ++ // Purpur end - config for turning bundles into functional quivers + + for (int i = 0; i < this.inventory.getContainerSize(); i++) { + ItemStack itemStack = this.inventory.getItem(i); ++ // Purpur start - config for turning bundles into functional quivers ++ if (useBundleItemStack) { ++ bundleProjectiles = itemStack.getItem() instanceof net.minecraft.world.item.BundleItem ? getProjectileFromBundle(itemStack, supportedProjectiles) : ItemStack.EMPTY; ++ if (!bundleProjectiles.isEmpty()) return bundleProjectiles; ++ } ++ // Purpur end - config for turning bundles into functional quivers + if (supportedProjectiles.test(itemStack)) { + return itemStack; + } +@@ -2143,6 +2162,39 @@ public abstract class Player extends Avatar implements ContainerUser { + return this.hasInfiniteMaterials() ? new ItemStack(Items.ARROW) : ItemStack.EMPTY; + } + ++ // Purpur start - config for turning bundles into functional quivers ++ private ItemStack getProjectileFromBundle(ItemStack itemStack, Predicate supportedProjectiles) { ++ if (!this.abilities.instabuild) { ++ net.minecraft.world.item.component.BundleContents bundleContents = itemStack.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); ++ if (bundleContents == null || bundleContents.isEmpty()) { ++ return ItemStack.EMPTY; ++ } ++ ++ Optional first = bundleContents.itemCopyStream().filter(supportedProjectiles).findFirst(); ++ ++ if (first.isEmpty()) { ++ return ItemStack.EMPTY; ++ } ++ ++ ItemStack firstItemStack = first.get(); ++ net.minecraft.world.item.component.BundleContents.Mutable mutable = new net.minecraft.world.item.component.BundleContents.Mutable(bundleContents); ++ ItemStack removedItemStack = mutable.removeOne(firstItemStack); ++ if (removedItemStack == null) { ++ return ItemStack.EMPTY; ++ } ++ ++ removedItemStack.shrink(1); ++ if (removedItemStack.getCount() != 0) { ++ mutable.tryInsert(removedItemStack); ++ } ++ itemStack.set(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS, mutable.toImmutable()); ++ firstItemStack.setCount(1); ++ return firstItemStack; ++ } ++ return ItemStack.EMPTY; ++ } ++ // Purpur end - config for turning bundles into functional quivers ++ + @Override + public Vec3 getRopeHoldPosition(final float partialTickTime) { + double xOff = 0.22 * (this.getMainArm() == HumanoidArm.RIGHT ? -1.0 : 1.0); +diff --git a/net/minecraft/world/item/BowItem.java b/net/minecraft/world/item/BowItem.java +index b8ec51d5f50c15f2cd74733f762b4ddc71b573cb..6df2b4e93d274ff2f06d18e22d1e02a5bcb62778 100644 +--- a/net/minecraft/world/item/BowItem.java ++++ b/net/minecraft/world/item/BowItem.java +@@ -25,7 +25,7 @@ public class BowItem extends ProjectileWeaponItem { + @Override + public boolean releaseUsing(final ItemStack itemStack, final Level level, final LivingEntity entity, final int remainingTime) { + if (entity instanceof Player player) { +- ItemStack projectile = player.getProjectile(itemStack); ++ ItemStack projectile = player.getProjectile(itemStack, level.purpurConfig.bowUseBundleComponentAsQuiver, level.purpurConfig.bowUseBundleItemAsQuiver); // Purpur - config for turning bundles into functional quivers + // Purpur start - Infinity bow settings + if (level.purpurConfig.infinityWorksWithoutArrows && projectile.isEmpty() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemStack) > 0) { + projectile = new ItemStack(Items.ARROW); +diff --git a/net/minecraft/world/item/BundleItem.java b/net/minecraft/world/item/BundleItem.java +index f64ecce370edc04c363db8bce7eda473e2764d27..0094d21429195811e8ae98e3d4e41f31c9316fd3 100644 +--- a/net/minecraft/world/item/BundleItem.java ++++ b/net/minecraft/world/item/BundleItem.java +@@ -143,7 +143,7 @@ public class BundleItem extends Item { + } + + private void dropContent(final Level level, final Player player, final ItemStack itemStack) { +- if (this.dropContent(itemStack, player)) { ++ if (!(level.purpurConfig.bowUseBundleItemAsQuiver || level.purpurConfig.crossbowUseBundleItemAsQuiver) && this.dropContent(itemStack, player)) { // Purpur - config for turning bundles into functional quivers + playDropContentsSound(level, player); + player.awardStat(Stats.ITEM_USED.get(this)); + } +diff --git a/net/minecraft/world/item/CrossbowItem.java b/net/minecraft/world/item/CrossbowItem.java +index d54f67ac95b1f93ea6d3db10160c68def11acc4e..1a376ce0a946cb27bb801da2e1c10be430fd2f75 100644 +--- a/net/minecraft/world/item/CrossbowItem.java ++++ b/net/minecraft/world/item/CrossbowItem.java +@@ -68,7 +68,7 @@ public class CrossbowItem extends ProjectileWeaponItem { + if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { + this.performShooting(level, player, hand, itemStack, getShootingPower(chargedProjectiles), (float) level.purpurConfig.crossbowProjectileOffset, null); // Purpur - Projectile offset config + return InteractionResult.CONSUME; +- } else if (!player.getProjectile(itemStack).isEmpty()) { ++ } else if (!player.getProjectile(itemStack).isEmpty()) { // Purpur - config for turning bundles into functional quivers + this.startSoundPlayed = false; + this.midLoadSoundPlayed = false; + player.startUsingItem(hand); +@@ -95,7 +95,7 @@ public class CrossbowItem extends ProjectileWeaponItem { + } + + private static boolean tryLoadProjectiles(final LivingEntity shooter, final ItemStack heldItem, final boolean consume) { +- List drawn = draw(heldItem, shooter.getProjectile(heldItem), shooter, consume); ++ List drawn = draw(heldItem, shooter.getProjectile(heldItem, shooter.level().purpurConfig.crossbowUseBundleComponentAsQuiver, shooter.level().purpurConfig.crossbowUseBundleItemAsQuiver), shooter, consume); // Purpur - config for turning bundles into functional quivers + // Paper end - Add EntityLoadCrossbowEvent + if (!drawn.isEmpty()) { + heldItem.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.ofNonEmpty(drawn)); +diff --git a/net/minecraft/world/item/component/BundleContents.java b/net/minecraft/world/item/component/BundleContents.java +index 8d925a64f21c1599ea558256b0efd93e99d95fac..f85a32ac4f42422c4baefc7e6721526af0a10795 100644 +--- a/net/minecraft/world/item/component/BundleContents.java ++++ b/net/minecraft/world/item/component/BundleContents.java +@@ -175,7 +175,12 @@ public final class BundleContents implements TooltipComponent { + } + + private int findStackIndex(final ItemStack itemsToAdd) { +- if (!itemsToAdd.isStackable()) { ++ // Purpur start - config for turning bundles into functional quivers ++ return this.findStackIndex(itemsToAdd, false); ++ } ++ private int findStackIndex(final ItemStack itemsToAdd, boolean skipStackableCheck) { ++ if (!skipStackableCheck && !itemsToAdd.isStackable()) { ++ // Purpur end - config for turning bundles into functional quivers + return BundleContents.NO_STACK_INDEX; + } + +@@ -253,12 +258,20 @@ public final class BundleContents implements TooltipComponent { + } + + public @Nullable ItemStack removeOne() { ++ // Purpur start - config for turning bundles into functional quivers ++ return this.removeOne(null); ++ } ++ public @Nullable ItemStack removeOne(ItemStack itemStack2) { ++ // Purpur end - config for turning bundles into functional quivers + if (this.items.isEmpty()) { + return null; + } + + int removeIndex = this.indexIsOutsideAllowedBounds(this.selectedItem) ? 0 : this.selectedItem; +- ItemStack stack = this.items.remove(removeIndex).copy(); ++ // Purpur start - config for turning bundles into functional quivers ++ int stackIndex = itemStack2 != null ? this.findStackIndex(itemStack2, true) : -1; ++ ItemStack stack = this.items.remove(stackIndex == -1 ? removeIndex : stackIndex).copy(); ++ // Purpur end - config for turning bundles into functional quivers + this.weight = this.weight.subtract(BundleContents.getWeight(stack).getOrThrow().multiplyBy(Fraction.getFraction(stack.getCount(), 1))); + this.toggleSelectedItem(BundleContents.NO_SELECTED_ITEM_INDEX); + return stack; diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java index 1254649c4..1f2bf12d7 100644 --- a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -303,6 +303,10 @@ private void infinityArrowsSettings() { public boolean snowballExtinguishesFire = false; public boolean snowballExtinguishesCandles = false; public boolean snowballExtinguishesCampfires = false; + public boolean bowUseBundleItemAsQuiver = false; + public boolean bowUseBundleComponentAsQuiver = false; + public boolean crossbowUseBundleItemAsQuiver = false; + public boolean crossbowUseBundleComponentAsQuiver = false; private void itemSettings() { itemImmuneToCactus.clear(); getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { @@ -355,6 +359,10 @@ private void itemSettings() { snowballExtinguishesFire = getBoolean("gameplay-mechanics.item.snowball.extinguish.fire", snowballExtinguishesFire); snowballExtinguishesCandles = getBoolean("gameplay-mechanics.item.snowball.extinguish.candles", snowballExtinguishesCandles); snowballExtinguishesCampfires = getBoolean("gameplay-mechanics.item.snowball.extinguish.campfires", snowballExtinguishesCampfires); + bowUseBundleItemAsQuiver = getBoolean("gameplay-mechanics.item.bow.use-bundle-as-quiver.item", bowUseBundleItemAsQuiver); + bowUseBundleComponentAsQuiver = getBoolean("gameplay-mechanics.item.bow.use-bundle-as-quiver.component", bowUseBundleComponentAsQuiver); + crossbowUseBundleItemAsQuiver = getBoolean("gameplay-mechanics.item.crossbow.use-bundle-as-quiver.item", crossbowUseBundleItemAsQuiver); + crossbowUseBundleComponentAsQuiver = getBoolean("gameplay-mechanics.item.crossbow.use-bundle-as-quiver.component", crossbowUseBundleComponentAsQuiver); } public double minecartMaxSpeed = 0.4D;