From 8c744dedf77ac35569176fd1d855fd10d8d10a42 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:16:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=AD=BB=E4=BA=A1=E4=BF=9D?= =?UTF-8?q?=E7=95=99/=E6=8D=9F=E6=AF=81=E7=89=A9=E5=93=81=E9=80=89?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/listener/ItemEventListener.java | 123 ++++++++++++++++-- .../core/item/AbstractItemManager.java | 28 +++- .../craftengine/core/item/ItemSettings.java | 30 +++++ .../item/modifier/ComponentsModifier.java | 2 +- gradle.properties | 2 +- 5 files changed, 173 insertions(+), 12 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index 4d7e6e374..5d3d4afa8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemSettings; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; @@ -54,20 +55,16 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; -import org.bukkit.inventory.EnchantingInventory; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; public class ItemEventListener implements Listener { private final BukkitCraftEngine plugin; @@ -630,4 +627,114 @@ public class ItemEventListener implements Listener { } event.setCurrentItem((ItemStack) result.finalItem().getItem()); } + + @SuppressWarnings("DuplicatedCode") + @EventHandler(ignoreCancelled = true) + public void onPlayerDeath(PlayerDeathEvent event) { + BukkitItemManager instance = BukkitItemManager.instance(); + + // 处理损毁物品 + if (event.getKeepInventory()) { + if (!instance.featureFlag$destroyOnDeathChance()) return; + + Random random = ThreadLocalRandom.current(); + PlayerInventory inventory = event.getPlayer().getInventory(); + for (ItemStack item : inventory.getContents()) { + if (item == null) continue; + + Optional> optional = instance.wrap(item).getCustomItem(); + if (optional.isEmpty()) continue; + + CustomItem customItem = optional.get(); + ItemSettings settings = customItem.settings(); + float destroyChance = settings.destroyOnDeathChance(); + if (destroyChance <= 0f) continue; + + int totalAmount = item.getAmount(); + int destroyCount = 0; + + for (int i = 0; i < totalAmount; i++) { + float rand = random.nextFloat(); + // 判断是否损毁 + if (destroyChance > 0f && rand < destroyChance) { + destroyCount++; + } + } + if (destroyCount != 0) { + item.setAmount(totalAmount - destroyCount); + } + } + } + // 处理保留 + 损毁物品 + else { + if (!instance.featureFlag$keepOnDeathChance() && !instance.featureFlag$destroyOnDeathChance()) return; + Random random = ThreadLocalRandom.current(); + + List itemsToKeep = event.getItemsToKeep(); + List itemsToDrop = event.getDrops(); + + Iterator iterator = itemsToDrop.iterator(); + + while (iterator.hasNext()) { + ItemStack item = iterator.next(); + Optional> optional = instance.wrap(item).getCustomItem(); + if (optional.isEmpty()) continue; + + CustomItem customItem = optional.get(); + ItemSettings settings = customItem.settings(); + + float destroyChance = settings.destroyOnDeathChance(); + float keepChance = settings.keepOnDeathChance(); + + // 如果没有效果,跳过 + if (destroyChance <= 0f && keepChance <= 0f) continue; + + int totalAmount = item.getAmount(); + + int keepCount = 0; + int destroyCount = 0; + int dropCount = 0; + + for (int i = 0; i < totalAmount; i++) { + float rand = random.nextFloat(); + + // 先判断是否损毁 + if (destroyChance > 0f && rand < destroyChance) { + destroyCount++; + } + // 然后判断是否保留(在未损毁的物品中) + else if (keepChance > 0f && rand < (destroyChance + keepChance)) { + keepCount++; + } + // 否则掉落 + else { + dropCount++; + } + } + + // 处理结果 + if (destroyCount == totalAmount) { + iterator.remove(); + continue; + } + + if (keepCount == 0 && dropCount == 0) { + // 实际上不会发生这种情况 + continue; + } + + if (keepCount > 0) { + ItemStack keepItem = item.clone(); + keepItem.setAmount(keepCount); + itemsToKeep.add(keepItem); + } + + if (dropCount > 0) { + item.setAmount(dropCount); + } else { + iterator.remove(); + } + } + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 1af81c706..a119bcf42 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -67,6 +67,9 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 替代配方材料 protected final Map> ingredientSubstitutes = new HashMap<>(); + protected boolean featureFlag$keepOnDeathChance = false; + protected boolean featureFlag$destroyOnDeathChance = false; + protected AbstractItemManager(CraftEngine plugin) { super(plugin); this.itemParser = new ItemParser(); @@ -133,6 +136,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl @Override public void unload() { super.clearModelsToGenerate(); + this.clearFeatureFlags(); this.customItemsById.clear(); this.customItemsByPath.clear(); this.cachedCustomItemSuggestions.clear(); @@ -147,6 +151,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl this.ingredientSubstitutes.clear(); } + private void clearFeatureFlags() { + this.featureFlag$keepOnDeathChance = false; + this.featureFlag$destroyOnDeathChance = false; + } + @Override public Map equipments() { return Collections.unmodifiableMap(this.equipments); @@ -205,12 +214,13 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); } // tags - Set tags = customItem.settings().tags(); + ItemSettings settings = customItem.settings(); + Set tags = settings.tags(); for (Key tag : tags) { this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); } // ingredient substitutes - List substitutes = customItem.settings().ingredientSubstitutes(); + List substitutes = settings.ingredientSubstitutes(); if (!substitutes.isEmpty()) { for (Key key : substitutes) { if (VANILLA_ITEMS.contains(key)) { @@ -218,6 +228,12 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } } + if (settings.keepOnDeathChance != 0) { + this.featureFlag$keepOnDeathChance = true; + } + if (settings.destroyOnDeathChance != 0) { + this.featureFlag$destroyOnDeathChance = true; + } } return true; } @@ -317,6 +333,14 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl return VANILLA_ITEMS.contains(item); } + public boolean featureFlag$keepOnDeathChance() { + return featureFlag$keepOnDeathChance; + } + + public boolean featureFlag$destroyOnDeathChance() { + return featureFlag$destroyOnDeathChance; + } + protected abstract CustomItem.Builder createPlatformItemBuilder(UniqueKey id, Key material, Key clientBoundMaterial); protected abstract void registerArmorTrimPattern(Collection equipments); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 64a67c8dd..00c6601a2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -47,6 +47,8 @@ public class ItemSettings { Color dyeColor; @Nullable Color fireworkColor; + float keepOnDeathChance = 0f; + float destroyOnDeathChance = 0f; Map, Object> customData = new IdentityHashMap<>(4); private ItemSettings() {} @@ -109,6 +111,8 @@ public class ItemSettings { newSettings.dyeColor = settings.dyeColor; newSettings.fireworkColor = settings.fireworkColor; newSettings.ingredientSubstitutes = settings.ingredientSubstitutes; + newSettings.keepOnDeathChance = settings.keepOnDeathChance; + newSettings.destroyOnDeathChance = settings.destroyOnDeathChance; newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -231,6 +235,14 @@ public class ItemSettings { return this.compostProbability; } + public float keepOnDeathChance() { + return this.keepOnDeathChance; + } + + public float destroyOnDeathChance() { + return this.destroyOnDeathChance; + } + public ItemSettings fireworkColor(Color color) { this.fireworkColor = color; return this; @@ -331,6 +343,16 @@ public class ItemSettings { return this; } + public ItemSettings keepOnDeathChance(float keepChance) { + this.keepOnDeathChance = keepChance; + return this; + } + + public ItemSettings destroyOnDeathChance(float destroyChance) { + this.destroyOnDeathChance = destroyChance; + return this; + } + @FunctionalInterface public interface Modifier { @@ -361,6 +383,14 @@ public class ItemSettings { boolean bool = ResourceConfigUtils.getAsBoolean(value, "enchantable"); return settings -> settings.canEnchant(bool); })); + registerFactory("keep-on-death-chance", (value -> { + float chance = ResourceConfigUtils.getAsFloat(value, "keep-on-death-chance"); + return settings -> settings.keepOnDeathChance(MiscUtils.clamp(chance, 0, 1)); + })); + registerFactory("destroy-on-death-chance", (value -> { + float chance = ResourceConfigUtils.getAsFloat(value, "destroy-on-death-chance"); + return settings -> settings.destroyOnDeathChance(MiscUtils.clamp(chance, 0, 1)); + })); registerFactory("renameable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "renameable"); return settings -> settings.renameable(bool); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index bb4213a32..57381eed6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -31,7 +31,7 @@ public class ComponentsModifier implements ItemDataModifier { } public List> components() { - return arguments; + return this.arguments; } private Tag parseValue(Object value) { diff --git a/gradle.properties b/gradle.properties index 1b2eee6a6..96cd697b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 sparrow_nbt_version=0.10.6 -sparrow_util_version=0.67 +sparrow_util_version=0.68 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8