diff --git a/CHANGELOG.md b/CHANGELOG.md index 6328edf..c523bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # You're in Grave Danger 2.0.8 +### Changes +* Item loss can now optionally be applied to modded inventories + +### Fixes +* Item loss will now not try and remove the same item twice, and count it as 2 +items (more reliable how much is lost) +* Running the /clear command after retrieving items from a grave no longer clears +the grave backup +* Soulbound now works with curios +* Graves being moved (like with carry-on mod) will now be detected when they reappear + --- # You're in Grave Danger 2.0.7 diff --git a/gradle.properties b/gradle.properties index be93f43..cba2734 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,9 +18,9 @@ minecraft_version=1.21.1 # as they do not follow standard versioning conventions. minecraft_version_range=[1.21,1.21.1) # The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=21.1.1 +neo_version=21.1.73 # The Neo version range can use any version of Neo as bounds -neo_version_range=[21.0.0-beta,) +neo_version_range=[21.1.0,) # The loader version range can only use the major version of FML as bounds loader_version_range=[4,) diff --git a/src/main/java/com/b1n_ry/yigd/Yigd.java b/src/main/java/com/b1n_ry/yigd/Yigd.java index 71261bb..c5d779d 100644 --- a/src/main/java/com/b1n_ry/yigd/Yigd.java +++ b/src/main/java/com/b1n_ry/yigd/Yigd.java @@ -19,6 +19,7 @@ import net.minecraft.core.UUIDUtil; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; @@ -72,7 +73,7 @@ public class Yigd private static final DeferredRegister> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.ATTACHMENT_TYPES, Yigd.MOD_ID); public static final Supplier> LAST_GROUND_POS = ATTACHMENT_TYPES.register("last_ground_pos", () -> AttachmentType.builder(() -> Vec3.ZERO).serialize(Vec3.CODEC).build()); - private static final DeferredRegister.DataComponents DATA_COMPONENTS = DeferredRegister.createDataComponents(Yigd.MOD_ID); + private static final DeferredRegister.DataComponents DATA_COMPONENTS = DeferredRegister.createDataComponents(Registries.DATA_COMPONENT_TYPE, Yigd.MOD_ID); public static final DeferredHolder, DataComponentType> GRAVE_ID = DATA_COMPONENTS.registerComponentType("grave_id", builder -> builder.persistent(UUIDUtil.CODEC).networkSynchronized(UUIDUtil.STREAM_CODEC)); public static final DeferredHolder, DataComponentType> GRAVE_LOCATION = DATA_COMPONENTS.registerComponentType("grave_location", builder -> builder.persistent(GlobalPos.CODEC).networkSynchronized(GlobalPos.STREAM_CODEC)); public static final DeathHandler DEATH_HANDLER = new DeathHandler(); diff --git a/src/main/java/com/b1n_ry/yigd/block/entity/GraveBlockEntity.java b/src/main/java/com/b1n_ry/yigd/block/entity/GraveBlockEntity.java index ac5ec83..735da08 100644 --- a/src/main/java/com/b1n_ry/yigd/block/entity/GraveBlockEntity.java +++ b/src/main/java/com/b1n_ry/yigd/block/entity/GraveBlockEntity.java @@ -199,7 +199,11 @@ public boolean hasCustomOutlineRendering(@NotNull Player player) { public static void tick(Level world, BlockPos pos, BlockState ignoredState, GraveBlockEntity be) { if (world.isClientSide) return; - if (be.component == null) return; + if (be.component == null) { + if (be.graveId == null) return; + DeathInfoManager.INSTANCE.getGrave(be.graveId).ifPresent(be::setComponent); + if (be.component == null) return; + } if (world.getGameTime() % 2400 == 0) cachedConfig = YigdConfig.getConfig(); // Reloads the config every 60 seconds YigdConfig.GraveConfig.GraveTimeout timeoutConfig = cachedConfig.graveConfig.graveTimeout; diff --git a/src/main/java/com/b1n_ry/yigd/compat/AccessoriesCompat.java b/src/main/java/com/b1n_ry/yigd/compat/AccessoriesCompat.java index aac6e1e..70f8bd8 100644 --- a/src/main/java/com/b1n_ry/yigd/compat/AccessoriesCompat.java +++ b/src/main/java/com/b1n_ry/yigd/compat/AccessoriesCompat.java @@ -250,20 +250,20 @@ public NonNullList storeToPlayer(ServerPlayer player) { for (int i = 0; i < inventorySlot.normal.size(); i++) { Tuple pair = inventorySlot.normal.get(i); if (i >= normalAccessories.getContainerSize()) { - extraItems.add(pair.getA()); + extraItems.add(pair.getA().copy()); continue; } - normalAccessories.setItem(i, pair.getA()); + normalAccessories.setItem(i, pair.getA().copy()); } for (int i = 0; i < inventorySlot.cosmetic.size(); i++) { Tuple pair = inventorySlot.cosmetic.get(i); if (i >= cosmeticAccessories.getContainerSize()) { - extraItems.add(pair.getA()); + extraItems.add(pair.getA().copy()); continue; } - cosmeticAccessories.setItem(i, pair.getA()); + cosmeticAccessories.setItem(i, pair.getA().copy()); } } diff --git a/src/main/java/com/b1n_ry/yigd/compat/CosmeticArmorCompat.java b/src/main/java/com/b1n_ry/yigd/compat/CosmeticArmorCompat.java index 855b5ba..2297d7d 100644 --- a/src/main/java/com/b1n_ry/yigd/compat/CosmeticArmorCompat.java +++ b/src/main/java/com/b1n_ry/yigd/compat/CosmeticArmorCompat.java @@ -70,7 +70,7 @@ public NonNullList storeToPlayer(ServerPlayer player) { for (int i = 0; i < cosArmor.getContainerSize(); i++) { if (i >= this.inventory.size()) break; - ItemStack stack = this.inventory.get(i).getA(); + ItemStack stack = this.inventory.get(i).getA().copy(); if (cosArmor.getItem(i).isEmpty()) { cosArmor.setItem(i, stack); } else { diff --git a/src/main/java/com/b1n_ry/yigd/compat/CuriosCompat.java b/src/main/java/com/b1n_ry/yigd/compat/CuriosCompat.java index 03a9da8..246f0ae 100644 --- a/src/main/java/com/b1n_ry/yigd/compat/CuriosCompat.java +++ b/src/main/java/com/b1n_ry/yigd/compat/CuriosCompat.java @@ -15,6 +15,7 @@ import net.neoforged.neoforge.common.NeoForge; import top.theillusivec4.curios.api.CuriosApi; import top.theillusivec4.curios.api.SlotContext; +import top.theillusivec4.curios.api.event.DropRulesEvent; import top.theillusivec4.curios.api.type.capability.ICurio; import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler; import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler; @@ -251,39 +252,50 @@ public NonNullList storeToPlayer(ServerPlayer player) { for (int i = 0; i < slotEntry.normal.size(); i++) { Tuple tuple = slotEntry.normal.get(i); if (i >= normalEquipped.getSlots()) { - extraItems.add(tuple.getA()); + extraItems.add(tuple.getA().copy()); continue; } - normalEquipped.setStackInSlot(i, tuple.getA()); + normalEquipped.setStackInSlot(i, tuple.getA().copy()); } for (int i = 0; i < slotEntry.cosmetic.size(); i++) { Tuple tuple = slotEntry.cosmetic.get(i); if (i >= cosmeticEquipped.getSlots()) { - extraItems.add(tuple.getA()); + extraItems.add(tuple.getA().copy()); continue; } - cosmeticEquipped.setStackInSlot(i, tuple.getA()); + cosmeticEquipped.setStackInSlot(i, tuple.getA().copy()); } } return extraItems; } - private ICurio.DropRule getDropRule(ItemStack stack, String key, int index, DeathContext context, boolean cosmetic) { + private ICurio.DropRule getDropRule(ItemStack stack, String key, int index, DeathContext context, boolean cosmetic, List, ICurio.DropRule>> overrides) { + for (Tuple, ICurio.DropRule> t : overrides) { + if (t.getA().test(stack)) { + return t.getB(); + } + } Optional iCurio = CuriosApi.getCurio(stack); return iCurio.map(curio -> curio.getDropRule(new SlotContext(key, context.player(), index, cosmetic, false), context.deathSource(), 0, true)).orElse(ICurio.DropRule.DEFAULT); } @Override public void handleDropRules(DeathContext context) { + ServerPlayer player = context.player(); + List, ICurio.DropRule>> overrides = new ArrayList<>(); + CuriosApi.getCuriosInventory(player).ifPresent(handler -> { + DropRulesEvent event = NeoForge.EVENT_BUS.post(new DropRulesEvent(player, handler, context.deathSource(), 0, false)); + overrides.addAll(event.getOverrides()); + }); for (Map.Entry entry : this.inventory.entrySet()) { String key = entry.getKey(); CuriosSlotEntry inventorySlot = entry.getValue(); for (int i = 0; i < inventorySlot.normal.size(); i++) { Tuple pair = inventorySlot.normal.get(i); ItemStack stack = pair.getA(); - DropRule dropRule = switch(this.getDropRule(stack, key, i, context, false)) { + DropRule dropRule = switch(this.getDropRule(stack, key, i, context, false, overrides)) { case DESTROY -> DropRule.DESTROY; case ALWAYS_KEEP -> DropRule.KEEP; default -> { @@ -300,7 +312,7 @@ public void handleDropRules(DeathContext context) { for (int i = 0; i < inventorySlot.cosmetic.size(); i++) { Tuple pair = inventorySlot.cosmetic.get(i); ItemStack stack = pair.getA(); - DropRule dropRule = switch(this.getDropRule(stack, key, i, context, true)) { + DropRule dropRule = switch(this.getDropRule(stack, key, i, context, true, overrides)) { case DESTROY -> DropRule.DESTROY; case ALWAYS_KEEP -> DropRule.KEEP; default -> { diff --git a/src/main/java/com/b1n_ry/yigd/compat/TravelersBackpackCompat.java b/src/main/java/com/b1n_ry/yigd/compat/TravelersBackpackCompat.java index b4cf62a..5b408d8 100644 --- a/src/main/java/com/b1n_ry/yigd/compat/TravelersBackpackCompat.java +++ b/src/main/java/com/b1n_ry/yigd/compat/TravelersBackpackCompat.java @@ -97,7 +97,7 @@ public NonNullList merge(CompatComponent mergingComponent, ServerP public NonNullList storeToPlayer(ServerPlayer player) { if (this.inventory.getA().isEmpty()) return NonNullList.create(); - AttachmentUtils.equipBackpack(player, this.inventory.getA()); + AttachmentUtils.equipBackpack(player, this.inventory.getA().copy()); return NonNullList.create(); } diff --git a/src/main/java/com/b1n_ry/yigd/components/GraveComponent.java b/src/main/java/com/b1n_ry/yigd/components/GraveComponent.java index 5a72474..ddf84b5 100644 --- a/src/main/java/com/b1n_ry/yigd/components/GraveComponent.java +++ b/src/main/java/com/b1n_ry/yigd/components/GraveComponent.java @@ -658,6 +658,7 @@ public void onDestroyed() { YigdConfig config = YigdConfig.getConfig(); + Yigd.LOGGER.info("Grave belonging to {} was detected destroyed at X: {}, Y: {}, Z: {} / {}", owner.getGameProfile().getName(), this.pos.getX(), this.pos.getY(), this.pos.getZ(), this.worldRegistryKey.location()); if (config.graveConfig.notifyOwnerIfDestroyed) { owner.sendSystemMessage(Component.translatable("text.yigd.message.grave_destroyed")); } diff --git a/src/main/java/com/b1n_ry/yigd/components/InventoryComponent.java b/src/main/java/com/b1n_ry/yigd/components/InventoryComponent.java index 940ff4f..7279f99 100644 --- a/src/main/java/com/b1n_ry/yigd/components/InventoryComponent.java +++ b/src/main/java/com/b1n_ry/yigd/components/InventoryComponent.java @@ -211,7 +211,8 @@ private void loseRandomItem() { YigdConfig.InventoryConfig.ItemLossConfig itemLoss = config.inventoryConfig.itemLoss; List itemSlots = new ArrayList<>(); - for (int i = 0; i < this.items.size(); i++) { + int vanillaLimit = this.items.size(); + for (int i = 0; i < vanillaLimit; i++) { Tuple pair = this.items.get(i); ItemStack stack = pair.getA(); if (stack.isEmpty()) continue; @@ -220,6 +221,18 @@ private void loseRandomItem() { itemSlots.add(i); } + NonNullList extraItems = NonNullList.create(); + if (itemLoss.includeModdedInventories) { + for (CompatComponent compatComponent : this.modInventoryItems.values()) { + for (Tuple tuple : compatComponent.getAsStackDropList()) { + if (tuple.getA().isEmpty()) continue; + extraItems.add(tuple.getA()); + } + } + for (int i = 0; i < extraItems.size(); i++) { + itemSlots.add(vanillaLimit + i); + } + } if (itemSlots.isEmpty()) return; @@ -227,12 +240,23 @@ private void loseRandomItem() { int slot = itemSlots.get(random); if (itemLoss.affectStacks) { - this.items.get(slot).setB(DropRule.DESTROY); + if (slot >= vanillaLimit) { + ItemStack toBeRemoved = extraItems.get(slot - vanillaLimit); + this.handleItemPairs(s -> !s.equals("vanilla"), (stack, s, pair) -> { + if (stack.equals(toBeRemoved)) pair.setB(DropRule.DESTROY); + }); + } else { + this.items.get(slot).setB(DropRule.DESTROY); + } } else { - ItemStack stack = this.items.get(slot).getA(); + ItemStack stack = slot >= vanillaLimit ? extraItems.get(slot - vanillaLimit) : this.items.get(slot).getA(); stack.shrink(1); } + ItemStack decreased = this.items.get(slot).getA(); + if (decreased.isEmpty() || decreased.getCount() == 0) { + itemSlots.remove(Integer.valueOf(slot)); // Make sure we can't lose this item again + } } public void dropAll(ServerLevel world, Vec3 pos) { @@ -539,7 +563,7 @@ public NonNullList applyToPlayer(ServerPlayer player) { playerInvIndex = groupIndex + invMainSize + invArmorSize + invOffHandSize; } - ItemStack stack = this.items.get(i).getA(); + ItemStack stack = this.items.get(i).getA().copy(); if (playerInvIndex >= inventory.getContainerSize() || playerInvIndex == -1) { extraItems.add(stack); diff --git a/src/main/java/com/b1n_ry/yigd/config/YigdConfig.java b/src/main/java/com/b1n_ry/yigd/config/YigdConfig.java index b1983f2..b987572 100644 --- a/src/main/java/com/b1n_ry/yigd/config/YigdConfig.java +++ b/src/main/java/com/b1n_ry/yigd/config/YigdConfig.java @@ -67,6 +67,7 @@ public static class ItemLossConfig { public int percentChanceOfLoss = 50; @Comment("If true, you can lose soulbound items from the item loss feature") public boolean canLoseSoulbound = false; + public boolean includeModdedInventories = true; } } @@ -134,9 +135,10 @@ public static class GraveConfig { public boolean storeXp = true; @Comment("Inform player where the grave generated when respawning") public boolean informGraveLocation = true; - @Comment("If true, you HAVE to have one of `requiredItem` for a grave to generate. One of that item will then be consumed") + @Comment("If true, you HAVE to have `requiredItemCount` number of `requiredItem` for a grave to generate. That many of that item will then be consumed") public boolean requireItem = false; public String requiredItem = "yigd:grave"; + public int requiredItemCount = 1; // require shovel to open public boolean requireShovelToLoot = false; // retrieve method (list with enums) diff --git a/src/main/java/com/b1n_ry/yigd/events/ServerEventHandler.java b/src/main/java/com/b1n_ry/yigd/events/ServerEventHandler.java index 7ec82b9..f590262 100644 --- a/src/main/java/com/b1n_ry/yigd/events/ServerEventHandler.java +++ b/src/main/java/com/b1n_ry/yigd/events/ServerEventHandler.java @@ -15,6 +15,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.GameRules; +import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.living.LivingDeathEvent; @@ -56,7 +57,7 @@ public void serverStarted(ServerStartedEvent event) { DeathInfoManager.INSTANCE.setDirty(); } - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.LOWEST) public void afterRespawn(PlayerEvent.Clone event) { if (!event.isWasDeath()) return; diff --git a/src/main/java/com/b1n_ry/yigd/events/YigdServerEventHandler.java b/src/main/java/com/b1n_ry/yigd/events/YigdServerEventHandler.java index ecb3e6f..a20117f 100644 --- a/src/main/java/com/b1n_ry/yigd/events/YigdServerEventHandler.java +++ b/src/main/java/com/b1n_ry/yigd/events/YigdServerEventHandler.java @@ -293,7 +293,7 @@ public void allowGraveGenerationEvent(AllowGraveGenerationEvent event) { if (graveConfig.requireItem) { Item item = BuiltInRegistries.ITEM.get(ResourceLocation.parse(graveConfig.requiredItem)); - if (!grave.getInventoryComponent().removeItem(stack -> stack.is(item), 1)) { + if (!grave.getInventoryComponent().removeItem(stack -> stack.is(item), graveConfig.requiredItemCount)) { event.setAllowGeneration(false); return; } diff --git a/src/main/resources/assets/yigd/lang/en_us.json b/src/main/resources/assets/yigd/lang/en_us.json index c5511d8..3c1610b 100644 --- a/src/main/resources/assets/yigd/lang/en_us.json +++ b/src/main/resources/assets/yigd/lang/en_us.json @@ -104,6 +104,7 @@ "text.autoconfig.yigd.option.inventoryConfig.itemLoss.lossRangeTo": "Range To", "text.autoconfig.yigd.option.inventoryConfig.itemLoss.percentChanceOfLoss": "% Chance of Loss Per Item", "text.autoconfig.yigd.option.inventoryConfig.itemLoss.canLoseSoulbound": "Can Affect Soulbound", + "text.autoconfig.yigd.option.inventoryConfig.itemLoss.includeModdedInventories": "Enable Loss from Modded Inventories", "text.autoconfig.yigd.option.inventoryConfig.loseSoulboundLevelOnDeath": "Lose Soulbound Level on Death", "text.autoconfig.yigd.option.inventoryConfig.vanishingSlots": "\"Trash Slots\"", "text.autoconfig.yigd.option.inventoryConfig.soulboundSlots": "Soulbound Slots", @@ -119,6 +120,7 @@ "text.autoconfig.yigd.option.graveConfig.informGraveLocation": "Inform Grave Location", "text.autoconfig.yigd.option.graveConfig.requireItem": "Require Item", "text.autoconfig.yigd.option.graveConfig.requiredItem": "Required Item", + "text.autoconfig.yigd.option.graveConfig.requiredItemCount": "Required Item Count", "text.autoconfig.yigd.option.graveConfig.requireShovelToLoot": "Require Shovel to Loot", "text.autoconfig.yigd.option.graveConfig.retrieveMethods": "Retrieve-Methods", "text.autoconfig.yigd.option.graveConfig.retrieveMethods.onClick": "On Click",