diff --git a/src/main/java/red/jackf/lenientdeath/ClosestSafeBlock.java b/src/main/java/red/jackf/lenientdeath/ClosestSafeBlock.java new file mode 100644 index 0000000..7fb1a66 --- /dev/null +++ b/src/main/java/red/jackf/lenientdeath/ClosestSafeBlock.java @@ -0,0 +1,83 @@ +package red.jackf.lenientdeath; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.Fluids; + + +public class ClosestSafeBlock { + + public static GlobalPos find(ServerLevel level, GlobalPos startPos) + { + var config = LenientDeath.CONFIG.instance().itemResilience; + + if (level == null) { + return null; // If there's no world available (e.g., client is null) + } + + BlockPos closestPos = null; + double closestDistanceSq = Double.MAX_VALUE; // Initialize to a large value + + double immediateProximitySq = 30 * 30; // 30 meters, squared + + BlockPos startBlockPos = startPos.pos(); + int xCenter = startBlockPos.getX(); + int yCenter = startBlockPos.getY(); + int zCenter = startBlockPos.getZ(); + + // Perform a spiral search + for (int radius = 0; radius <= config.closestSafeBlockSearchRange; radius++) { + for (int dx = -radius; dx <= radius; dx++) { + for (int dz = -radius; dz <= radius; dz++) { + if (Math.abs(dx) != radius && Math.abs(dz) != radius) { + // Skip positions not on the edge of the current radius + continue; + } + + for (int dy = -radius; dy <= radius; dy++) { // Include height variation + BlockPos checkPos = new BlockPos(xCenter + dx, yCenter + dy, zCenter + dz); + + if (isValidPosition(level, checkPos)) { + double distanceSq = startBlockPos.distSqr(checkPos); + + // Return immediately if within immediate proximity + if (distanceSq <= immediateProximitySq) { + return GlobalPos.of(level.dimension(), checkPos.above()); + } + + // Otherwise, track the closest position found + if (distanceSq < closestDistanceSq) { + closestDistanceSq = distanceSq; + closestPos = checkPos; + } + } + } + } + } + } + + // Return the closest valid position found or fallback to spawn + if (closestPos != null) { + return GlobalPos.of(level.dimension(), closestPos.above()); + } + + return GlobalPos.of(level.dimension(), level.getSharedSpawnPos()); + } + + public static boolean isValidPosition(ServerLevel level, BlockPos pos) { + var checkPosBlockState = level.getBlockState(pos); + var aboveCheckPosBlockState = level.getBlockState(pos.above()); + var aboveCheckPosFluidState = level.getFluidState(pos.above()); + + var isSolid = checkPosBlockState.isSolid(); + var isAirAbove = aboveCheckPosBlockState.isAir(); + var isLavaAbove = aboveCheckPosBlockState.is(Blocks.LAVA) || aboveCheckPosFluidState.is(Fluids.LAVA); + + return isSolid && isAirAbove && !isLavaAbove; + } + + + +} diff --git a/src/main/java/red/jackf/lenientdeath/ItemResilience.java b/src/main/java/red/jackf/lenientdeath/ItemResilience.java index 1813fd3..cba8c34 100644 --- a/src/main/java/red/jackf/lenientdeath/ItemResilience.java +++ b/src/main/java/red/jackf/lenientdeath/ItemResilience.java @@ -16,6 +16,7 @@ import red.jackf.lenientdeath.mixinutil.LDDeathContextHolder; import red.jackf.lenientdeath.mixinutil.LDGroundedPosHolder; + public class ItemResilience { private static final TagKey ITEMS_IMMUNE_TO = TagKey.create( Registries.DAMAGE_TYPE, @@ -37,6 +38,17 @@ public static boolean areItemsImmuneTo(DamageSource source) { var groundedPosHolder = (LDGroundedPosHolder) serverPlayer; var ctx = deathContextHolder.lenientdeath$getDeathContext(); var groundedPos = groundedPosHolder.lenientdeath$getLastGroundedPosition(); + + //If the groundedPos is not valid (e.g Air or Lava) then find a safe block. + if(!ClosestSafeBlock.isValidPosition(serverPlayer.serverLevel(),groundedPos.pos())) + { + //If there is no LastGroundedPosition or if the groundedPos is too far away from the death location, use the actual player location + if(groundedPos == null || groundedPos.pos().distSqr(serverPlayer.getOnPos()) >= 100) + groundedPos = GlobalPos.of(serverPlayer.serverLevel().dimension(), serverPlayer.getOnPos()); + + groundedPos = ClosestSafeBlock.find(serverPlayer.serverLevel(),groundedPos); + } + if (ctx != null && groundedPos != null && ctx.source().is(DamageTypes.FELL_OUT_OF_WORLD)) { return ifTrue.apply(ctx, groundedPos, serverPlayer); } @@ -44,11 +56,39 @@ public static boolean areItemsImmuneTo(DamageSource source) { return null; } + public static @Nullable T ifHandledLavaDeath( + Object player, + TriFunction ifTrue) { + if (LenientDeath.CONFIG.instance().itemResilience.lavaRecovery.mode == LenientDeathConfig.ItemResilience.LavaRecovery.Mode.closest_safe_location + && player instanceof ServerPlayer serverPlayer) { + var deathContextHolder = (LDDeathContextHolder) serverPlayer; + var groundedPosHolder = (LDGroundedPosHolder) serverPlayer; + var ctx = deathContextHolder.lenientdeath$getDeathContext(); + var groundedPos = groundedPosHolder.lenientdeath$getLastGroundedPosition(); + + //If there is no LastGroundedPosition or if the groundedPos is too far away from the death location, use the actual player location + if(groundedPos == null || groundedPos.pos().distSqr(serverPlayer.getOnPos()) >= 30) + groundedPos = GlobalPos.of(serverPlayer.serverLevel().dimension(), serverPlayer.getOnPos()); + + //Determine the closet safe block + var safePos = ClosestSafeBlock.find(serverPlayer.serverLevel(), groundedPos); + + if (ctx != null && safePos != null && ctx.source().is(DamageTypes.LAVA)) { + return ifTrue.apply(ctx, safePos, serverPlayer); + } + } + return null; + } + public static boolean shouldForceKeep(ServerPlayer player) { if (LenientDeath.CONFIG.instance().itemResilience.voidRecovery.mode == LenientDeathConfig.ItemResilience.VoidRecovery.Mode.preserve) { var deathContext = ((LDDeathContextHolder) player).lenientdeath$getDeathContext(); return deathContext != null && deathContext.source().is(DamageTypes.FELL_OUT_OF_WORLD); } + if (LenientDeath.CONFIG.instance().itemResilience.lavaRecovery.mode == LenientDeathConfig.ItemResilience.LavaRecovery.Mode.preserve) { + var deathContext = ((LDDeathContextHolder) player).lenientdeath$getDeathContext(); + return deathContext != null && deathContext.source().is(DamageTypes.LAVA); + } return false; } @@ -60,7 +100,17 @@ public static void onPlayerDeath(ServerPlayer serverPlayer) { Formatting.variable(groundedPos.pos().above().toShortString()), Formatting.variable(groundedPos.dimension().location().toString())) )); + return null; + }); + } + if (LenientDeath.CONFIG.instance().itemResilience.lavaRecovery.announce) { + ifHandledLavaDeath(serverPlayer, (ctx, groundedPos, serverPlayer1) -> { + serverPlayer1.sendSystemMessage(Formatting.infoLine( + Component.translatable("lenientdeath.itemResilience.announce", + Formatting.variable(groundedPos.pos().above().toShortString()), + Formatting.variable(groundedPos.dimension().location().toString())) + )); return null; }); } diff --git a/src/main/java/red/jackf/lenientdeath/command/subcommand/CommandConfig.java b/src/main/java/red/jackf/lenientdeath/command/subcommand/CommandConfig.java index 78ebac5..1628f2f 100644 --- a/src/main/java/red/jackf/lenientdeath/command/subcommand/CommandConfig.java +++ b/src/main/java/red/jackf/lenientdeath/command/subcommand/CommandConfig.java @@ -433,6 +433,7 @@ private static LiteralArgumentBuilder createItemResilience() )); root.then(makeVoidRecoveryNode()); + root.then(makeLavaRecoveryNode()); return root; } @@ -460,6 +461,30 @@ private static LiteralArgumentBuilder makeVoidRecoveryNode() return root; } + private static LiteralArgumentBuilder makeLavaRecoveryNode() { + var root = Commands.literal("lavaRecovery"); + + root.then(makeEnum( + "mode", + "itemResilience.lavaRecovery.mode", + WikiPage.ITEM_RESILIENCE, + LenientDeathConfig.ItemResilience.LavaRecovery.Mode.class, + config -> config.itemResilience.lavaRecovery.mode, + (config, newValue) -> config.itemResilience.lavaRecovery.mode = newValue + )); + + root.then(makeBoolean( + "announce", + "itemResilience.lavaRecovery.announce", + WikiPage.ITEM_RESILIENCE, + config -> config.itemResilience.lavaRecovery.announce, + (config, newValue) -> config.itemResilience.lavaRecovery.announce = newValue + )); + + return root; + } + + private static LiteralArgumentBuilder createPresetsNode() { var root = Commands.literal("presets"); diff --git a/src/main/java/red/jackf/lenientdeath/config/LenientDeathConfig.java b/src/main/java/red/jackf/lenientdeath/config/LenientDeathConfig.java index 5f9f007..8db04eb 100644 --- a/src/main/java/red/jackf/lenientdeath/config/LenientDeathConfig.java +++ b/src/main/java/red/jackf/lenientdeath/config/LenientDeathConfig.java @@ -208,6 +208,12 @@ public static class ItemResilience { Default: false""") public boolean allDeathItemsAreExplosionProof = false; + @Comment(""" + Dertermines the search range for getting the closest safe block when using LavaRecovery or when the last position of the VoidRecovery is invalid. + Options: Integer + Default: 256""") + public int closestSafeBlockSearchRange = 256; + @Comment(""" Features related to handling item drops when a player dies to the void; for example if they fall off the end island, or are playing SkyBlock.""") @@ -239,6 +245,37 @@ public enum Mode { preserve } } + + @Comment(""" + Features related to handling item drops when a player dies to swimming in lava; for example if they fall into a lava lake in the nether.""") + public LavaRecovery lavaRecovery = new LavaRecovery(); + + public static class LavaRecovery { + + @Comment(""" + How death drop items when the player is killed by lava should be handled? + Options: + - disabled (keeps items position) + - closest_safe_location (teleport to closest safe position (not in air or lava) + - preserve (keep items in the inventory even if they wouldn't normally; applies to everyone) + Default: closest_safe_location""") + public Mode mode = Mode.closest_safe_location; + + @Comment(""" + When a player dies to being in lava, should Lenient Death notify them where their items were moved to? Only + applies if mode = closest_safe_location. + This option exists because players who may not be aware of this feature probably would not look for + their items otherwise. + Options: true, false + Default: true""") + public boolean announce = true; + + public enum Mode { + disabled, + closest_safe_location, + preserve + } + } } @Comment(""" diff --git a/src/main/java/red/jackf/lenientdeath/config/Presets.java b/src/main/java/red/jackf/lenientdeath/config/Presets.java index 03c0f23..a7d6cbd 100644 --- a/src/main/java/red/jackf/lenientdeath/config/Presets.java +++ b/src/main/java/red/jackf/lenientdeath/config/Presets.java @@ -30,6 +30,7 @@ private static LenientDeathConfig makeDisabled() { config.droppedItemGlow.enabled = false; config.itemResilience.voidRecovery.mode = LenientDeathConfig.ItemResilience.VoidRecovery.Mode.disabled; + config.itemResilience.lavaRecovery.mode = LenientDeathConfig.ItemResilience.LavaRecovery.Mode.disabled; config.deathCoordinates.sendToServerLog = false; config.deathCoordinates.sendToDeadPlayer = false; @@ -59,6 +60,7 @@ private static LenientDeathConfig makeOnlyVisuals() { config.preserveItemsOnDeath.byItemType.enabled = false; config.itemResilience.voidRecovery.mode = LenientDeathConfig.ItemResilience.VoidRecovery.Mode.disabled; + config.itemResilience.lavaRecovery.mode = LenientDeathConfig.ItemResilience.LavaRecovery.Mode.disabled; return config; } @@ -71,6 +73,7 @@ private static LenientDeathConfig makeOnlyRandom() { config.preserveItemsOnDeath.randomizer.enabled = true; config.itemResilience.voidRecovery.mode = LenientDeathConfig.ItemResilience.VoidRecovery.Mode.disabled; + config.itemResilience.lavaRecovery.mode = LenientDeathConfig.ItemResilience.LavaRecovery.Mode.disabled; return config; } diff --git a/src/main/java/red/jackf/lenientdeath/mixins/itemresilience/ServerPlayerMixin.java b/src/main/java/red/jackf/lenientdeath/mixins/itemresilience/ServerPlayerMixin.java index 7ff0269..fee522a 100644 --- a/src/main/java/red/jackf/lenientdeath/mixins/itemresilience/ServerPlayerMixin.java +++ b/src/main/java/red/jackf/lenientdeath/mixins/itemresilience/ServerPlayerMixin.java @@ -11,6 +11,7 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; @@ -98,24 +99,61 @@ private void saveGroundedPos(CompoundTag tag, CallbackInfo ci) { @ModifyReceiver(method = "drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z")) private Level lenientdeath$moveIfVoidedAndEnabled(Level original, Entity entity) { - var targetLevel = ItemResilience.ifHandledVoidDeath(this, (deathContext, lastGroundedPos, player) -> { + ServerLevel targetLevel = null; + + // Handling void death scenario + var voidTargetLevel = ItemResilience.ifHandledVoidDeath(this, (deathContext, lastGroundedPos, player) -> { + if (!lastGroundedPos.dimension().equals(original.dimension())) { + return this.server.getLevel(lastGroundedPos.dimension()); + } else { + return null; + } + }); + + // Handling lava death scenario + var lavaTargetLevel = ItemResilience.ifHandledLavaDeath(this, (deathContext, lastGroundedPos, player) -> { if (!lastGroundedPos.dimension().equals(original.dimension())) { return this.server.getLevel(lastGroundedPos.dimension()); } else { return null; } }); + + // Assigning the target level if any of the scenarios is met + if (voidTargetLevel != null) { + targetLevel = voidTargetLevel; + } else if (lavaTargetLevel != null) { + targetLevel = lavaTargetLevel; + } + if (targetLevel != null) return targetLevel; return original; } @WrapOperation(method = "createItemStackToDrop", at = @At(value = "NEW", target = "(Lnet/minecraft/world/level/Level;DDDLnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) private ItemEntity spawnDeathItemAtDifferentPosition(Level level, double posX, double posY, double posZ, ItemStack itemStack, Operation original) { - ItemEntity item = ItemResilience.ifHandledVoidDeath(this, (deathContext, groundPos, player) -> { + + ItemEntity item = null; + + // Handling void death scenario + var voidDeathItem = ItemResilience.ifHandledVoidDeath(this, (deathContext, groundPos, player) -> { Vec3 pos = groundPos.pos().getCenter(); return original.call(this.server.getLevel(groundPos.dimension()), pos.x(), pos.y() + 1, pos.z(), itemStack); }); + // Handling lava death scenario + var lavaDeathItem = ItemResilience.ifHandledLavaDeath(this, (deathContext, groundPos, player) -> { + Vec3 pos = groundPos.pos().getCenter(); + return original.call(this.server.getLevel(groundPos.dimension()), pos.x(), pos.y() + 1, pos.z(), itemStack); + }); + + // Assigning the item if any of the scenarios is met + if (voidDeathItem != null) { + item = voidDeathItem; + } else if (lavaDeathItem != null) { + item = lavaDeathItem; + } + if (item == null) item = original.call(level, posX, posY, posZ, itemStack); return item; @@ -123,10 +161,19 @@ private ItemEntity spawnDeathItemAtDifferentPosition(Level level, double posX, d @ModifyReturnValue(method = "createItemStackToDrop", at = @At("RETURN")) private ItemEntity killDeathItemVelocity(ItemEntity original) { + + // Handling void death scenario ItemResilience.ifHandledVoidDeath(this, (deathContext, groundPos, player) -> { if (original != null) original.setDeltaMovement(Vec3.ZERO); return null; }); + + // Handling lava death scenario + ItemResilience.ifHandledLavaDeath(this, (deathContext, groundPos, player) -> { + if (original != null) original.setDeltaMovement(Vec3.ZERO); + return null; + }); + return original; } } diff --git a/src/main/resources/data/lenientdeath/lang/de_de.json b/src/main/resources/data/lenientdeath/lang/de_de.json new file mode 100644 index 0000000..005003a --- /dev/null +++ b/src/main/resources/data/lenientdeath/lang/de_de.json @@ -0,0 +1,49 @@ +{ + "lenientdeath.title": "Lenient Death", + "lenientdeath.command.perPlayer.playerDisabled": "Lenient Death ist für %s deaktiviert", + "lenientdeath.command.perPlayer.playerEnabled": "Lenient Death ist für %s aktiviert", + "lenientdeath.command.perPlayer.setPlayerDisabled": "Lenient Death für %s deaktiviert", + "lenientdeath.command.perPlayer.setPlayerEnabled": "Lenient Death für %s aktiviert", + "lenientdeath.command.perPlayer.setPlayerAlreadyDisabled": "Lenient Death ist für %s bereits deaktiviert", + "lenientdeath.command.perPlayer.setPlayerAlreadyEnabled": "Lenient Death ist für %s bereits aktiviert", + "lenientdeath.command.perPlayer.enableButton": "Aktivieren", + "lenientdeath.command.perPlayer.disableButton": "Deaktivieren", + "lenientdeath.command.perPlayer.handledByPermissions": "%s wird von einem Berechtigungs-Plugin verwaltet", + "lenientdeath.command.perPlayer.noPermissionToChangeSelf": "Du darfst deine eigenen Einstellungen nicht ändern", + "lenientdeath.command.perPlayer.noPermissionToChangeOthers": "Du darfst die Einstellungen anderer nicht ändern", + "lenientdeath.command.config.presetApplied": "Voreinstellung %s angewendet", + "lenientdeath.command.config.check": "%s: %s", + "lenientdeath.command.config.change": "%s: %s -> %s", + "lenientdeath.command.config.unchanged": "%s: %s (unverändert)", + "lenientdeath.command.config.list.empty": "(leer)", + "lenientdeath.command.config.list.alreadyContains": "%s: Enthält bereits %s", + "lenientdeath.command.config.list.doesNotContain": "%s: Enthält nicht %s", + "lenientdeath.command.config.unknownId": "Unbekannte ID: %s", + "lenientdeath.command.config.list.added": "%s: %s hinzugefügt", + "lenientdeath.command.config.list.removed": "%s: %s entfernt", + "lenientdeath.command.config.requiresWorldReload": "Erfordert ein Neuladen der Welt / einen Neustart des Servers, um wirksam zu werden", + "lenientdeath.command.config.clickToOpenWiki": "Klicke hier, um die Wiki-Seite für diese Option zu öffnen", + "lenientdeath.command.utilies.safeCheck.success": "%s wird erhalten bleiben", + "lenientdeath.command.utilies.safeCheck.random.noSplitting": "%s wird mit einer Chance von %d%% erhalten bleiben", + "lenientdeath.command.utilies.safeCheck.random.splitting": "%d%% von %s wird erhalten bleiben", + "lenientdeath.command.utilies.safeCheck.failure": "%s wird nicht erhalten bleiben", + "lenientdeath.command.utilies.listItemsInTag": "Gegenstände in %s:", + "lenientdeath.command.restoreInventory.empty": "%s hat keine Todesfälle gespeichert.", + "lenientdeath.command.restoreInventory.header": "Gespeicherte Todesfälle für %s:", + "lenientdeath.command.restoreInventory.timeAndPosition": "%s bei %s in %s", + "lenientdeath.command.restoreInventory.restore": "Wiederherstellen", + "lenientdeath.command.restoreInventory.restore.hover": "Stellt das angegebene Inventar wieder her und lässt Gegenstände fallen, die nicht passen.", + "lenientdeath.command.restoreInventory.replace": "Ersetzen", + "lenientdeath.command.restoreInventory.replace.hover": "Stellt das angegebene Inventar und die XP-Stufe wieder her, wobei das aktuelle Inventar des Spielers entfernt wird.", + "lenientdeath.command.restoreInventory.itemsAndXp": "%d Gegenstände | %d XP-Punkte", + "lenientdeath.command.restoreInventory.indexOutOfRange": "Todesfall-Datensatz außerhalb des Bereichs: %d", + "lenientdeath.command.restoreInventory.success": "Das Inventar von %d wurde erfolgreich wiederhergestellt.", + "lenientdeath.command.restoreInventory.success.replace": "Das Inventar von %d wurde erfolgreich ersetzt.", + "lenientdeath.command.restoreInventory.time.secondsAgo": "vor %1$s Sekunde(n)", + "lenientdeath.command.restoreInventory.time.minutesAgo": "vor %1$s Minute(n)", + "lenientdeath.command.restoreInventory.time.hoursAgo": "vor %1$s Stunde(n)", + "lenientdeath.command.restoreInventory.time.daysAgo": "vor %1$s Tag(en)", + "lenientdeath.itemResilience.announce": "Deine Gegenstände wurden zu %s in %s verschoben.", + "lenientdeath.deathCoordinates": "Du bist gestorben bei %s, in %s.", + "tag.item.lenientdeath.safe": "Beim Tod behalten" +} diff --git a/src/main/resources/data/lenientdeath/lang/sxu_DE.json b/src/main/resources/data/lenientdeath/lang/sxu_DE.json new file mode 100644 index 0000000..22ac5ae --- /dev/null +++ b/src/main/resources/data/lenientdeath/lang/sxu_DE.json @@ -0,0 +1,49 @@ +{ + "lenientdeath.title": "Lenient Death", + "lenientdeath.command.perPlayer.playerDisabled": "Lenient Death isch für %s ausgeschalld", + "lenientdeath.command.perPlayer.playerEnabled": "Lenient Death isch für %s anghalld", + "lenientdeath.command.perPlayer.setPlayerDisabled": "Lenient Death fer %s ausgeschalld", + "lenientdeath.command.perPlayer.setPlayerEnabled": "Lenient Death fer %s anghalld", + "lenientdeath.command.perPlayer.setPlayerAlreadyDisabled": "Lenient Death isch fer %s schon ausgeschalld", + "lenientdeath.command.perPlayer.setPlayerAlreadyEnabled": "Lenient Death isch fer %s schon anghalld", + "lenientdeath.command.perPlayer.enableButton": "Anghalldn", + "lenientdeath.command.perPlayer.disableButton": "Ausschalldn", + "lenientdeath.command.perPlayer.handledByPermissions": "%s werd von nem Berechtigungs-Plugin gmachd", + "lenientdeath.command.perPlayer.noPermissionToChangeSelf": "Du darfsd dei egne Eistellunge net ännarn", + "lenientdeath.command.perPlayer.noPermissionToChangeOthers": "Du darfsd die Eistellunge von annren net ännarn", + "lenientdeath.command.config.presetApplied": "Voreschlachd %s angewänd", + "lenientdeath.command.config.check": "%s: %s", + "lenientdeath.command.config.change": "%s: %s -> %s", + "lenientdeath.command.config.unchanged": "%s: %s (unverändert)", + "lenientdeath.command.config.list.empty": "(leer)", + "lenientdeath.command.config.list.alreadyContains": "%s: Hat scho %s", + "lenientdeath.command.config.list.doesNotContain": "%s: Hot %s net", + "lenientdeath.command.config.unknownId": "Unbekannte ID: %s", + "lenientdeath.command.config.list.added": "%s: %s hinzugefoochd", + "lenientdeath.command.config.list.removed": "%s: %s rausgenomme", + "lenientdeath.command.config.requiresWorldReload": "Bruchd neugeladne Welld / Server-Neuschdard, dasses wirgd", + "lenientdeath.command.config.clickToOpenWiki": "Drugg hier, um die Wiki-Seide fer die Option uffzumache", + "lenientdeath.command.utilies.safeCheck.success": "%s bleibt erhaldn", + "lenientdeath.command.utilies.safeCheck.random.noSplitting": "%s bleibt mit ner Chance von %d%% erhaldn", + "lenientdeath.command.utilies.safeCheck.random.splitting": "%d%% von %s bleibt erhaldn", + "lenientdeath.command.utilies.safeCheck.failure": "%s bleibd net erhaldn", + "lenientdeath.command.utilies.listItemsInTag": "Gensche in %s:", + "lenientdeath.command.restoreInventory.empty": "%s hot keine Tode gspeicherd.", + "lenientdeath.command.restoreInventory.header": "Gspeicherd Todesfäll fer %s:", + "lenientdeath.command.restoreInventory.timeAndPosition": "%s bei %s in %s", + "lenientdeath.command.restoreInventory.restore": "Wiedaheemholn", + "lenientdeath.command.restoreInventory.restore.hover": "Hoold des ggebne Inventar widda, legt Gensche ab, wo nimmer nei passn.", + "lenientdeath.command.restoreInventory.replace": "Uustauschn", + "lenientdeath.command.restoreInventory.replace.hover": "Hoold des ggebne Inventar und XP widda und räumt des aktuelle Inventar leear.", + "lenientdeath.command.restoreInventory.itemsAndXp": "%d Gensche | %d XP-Punke", + "lenientdeath.command.restoreInventory.indexOutOfRange": "Todesfall-Nummr außerhalb vom Bereich: %d", + "lenientdeath.command.restoreInventory.success": "Das Inventar von %d isch widdaheemghoold worchn.", + "lenientdeath.command.restoreInventory.success.replace": "Das Inventar von %d isch uustauscht worchn.", + "lenientdeath.command.restoreInventory.time.secondsAgo": "vor %1$s Sekund(e)", + "lenientdeath.command.restoreInventory.time.minutesAgo": "vor %1$s Minut(e)", + "lenientdeath.command.restoreInventory.time.hoursAgo": "vor %1$s Schdund(e)", + "lenientdeath.command.restoreInventory.time.daysAgo": "vor %1$s Dog(e)", + "lenientdeath.itemResilience.announce": "Dei Gensche sinn nach %s in %s gebrachd worchn.", + "lenientdeath.deathCoordinates": "Du bisd gestorbm bei %s, in %s.", + "tag.item.lenientdeath.safe": "Beim Schdorbn behallde" +}