diff --git a/src/main/java/info/shylie/ashes/AshesRegistry.java b/src/main/java/info/shylie/ashes/AshesRegistry.java index bcff47c..2574cb2 100644 --- a/src/main/java/info/shylie/ashes/AshesRegistry.java +++ b/src/main/java/info/shylie/ashes/AshesRegistry.java @@ -8,7 +8,8 @@ import info.shylie.ashes.entity.AshenGolemEntity; import info.shylie.ashes.item.RuneItem; import info.shylie.ashes.rune.param.BlockPosRune; import info.shylie.ashes.rune.param.ItemRune; -import info.shylie.ashes.rune.task.*; +import info.shylie.ashes.rune.task.composite.*; +import info.shylie.ashes.rune.task.leaf.*; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -49,8 +50,14 @@ public class AshesRegistry { public static final RegistryObject> SELECTOR_RUNE; public static final RegistryObject> STEP_SEQUENCE_RUNE; public static final RegistryObject> STEP_SELECTOR_RUNE; + public static final RegistryObject> INVERTER_RUNE; public static final RegistryObject> MOVE_RUNE; + public static final RegistryObject> TAKE_ITEM_RUNE; + public static final RegistryObject> PUT_ITEM_RUNE; + public static final RegistryObject> INTERACT_RUNE; + + public static final RegistryObject> HAS_ITEM_RUNE; static { HEATED_ASHY_INGOT = ITEMS.register( @@ -89,8 +96,13 @@ public class AshesRegistry { SELECTOR_RUNE = RUNES.register("selector", RuneType.Builder.of(SelectorRune::new)::build); STEP_SEQUENCE_RUNE = RUNES.register("step_sequence", RuneType.Builder.of(StepSequenceRune::new)::build); STEP_SELECTOR_RUNE = RUNES.register("step_selector", RuneType.Builder.of(StepSelectorRune::new)::build); + INVERTER_RUNE = RUNES.register("inverter", RuneType.Builder.of(InverterRune::new)::build); MOVE_RUNE = RUNES.register("move", RuneType.Builder.of(MoveRune::new)::build); + TAKE_ITEM_RUNE = RUNES.register("take_item", RuneType.Builder.of(TakeItemRune::new)::build); + PUT_ITEM_RUNE = RUNES.register("put_item", RuneType.Builder.of(PutItemRune::new)::build); + INTERACT_RUNE = RUNES.register("interact", RuneType.Builder.of(InteractRune::new)::build); + HAS_ITEM_RUNE = RUNES.register("has_item", RuneType.Builder.of(HasItemRune::new)::build); } public static RegistryObject registerSpawnEgg(RegistryObject> type, int bg, int fg) { diff --git a/src/main/java/info/shylie/ashes/Utils.java b/src/main/java/info/shylie/ashes/Utils.java new file mode 100644 index 0000000..5a316a7 --- /dev/null +++ b/src/main/java/info/shylie/ashes/Utils.java @@ -0,0 +1,14 @@ +package info.shylie.ashes; + +import java.lang.reflect.Array; + +public class Utils { + public static T[] concat(T[] b, T... a) { + @SuppressWarnings("unchecked") + T[] c = (T[])Array.newInstance(a.getClass().getComponentType(), a.length + b.length); + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + + return c; + } +} diff --git a/src/main/java/info/shylie/ashes/api/golem/IGolem.java b/src/main/java/info/shylie/ashes/api/golem/IGolem.java index 29bc461..f61cf5e 100644 --- a/src/main/java/info/shylie/ashes/api/golem/IGolem.java +++ b/src/main/java/info/shylie/ashes/api/golem/IGolem.java @@ -1,10 +1,16 @@ package info.shylie.ashes.api.golem; +import com.mojang.authlib.GameProfile; import info.shylie.ashes.api.rune.TaskRune; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.animal.AbstractGolem; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.common.util.FakePlayerFactory; public interface IGolem { + float MAX_INTERACT_DISTANCE = 2; + AbstractGolem asEntity(); TaskRune getTask(); @@ -16,4 +22,15 @@ public interface IGolem { task.update(this); } } + + default FakePlayer getFakePlayer() { + return FakePlayerFactory.get((ServerLevel)asEntity().level(), new GameProfile( + asEntity().getUUID(), + asEntity().getDisplayName().getString() + )); + } + + default boolean canInteract(BlockPos pos) { + return pos != null && asEntity().distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) <= 3; + } } diff --git a/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java b/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java index 68d03e6..5c7635b 100644 --- a/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java +++ b/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java @@ -19,7 +19,7 @@ public abstract class CompositeTaskRune extends TaskRune { } public void add(TaskRune rune) { - if (rune == null) { return; } + if (rune == null || children.size() >= maxChildren()) { return; } children.add(rune); } @@ -74,7 +74,12 @@ public abstract class CompositeTaskRune extends TaskRune { } } - protected abstract Status updateImpl2(IGolem golem); + protected int maxChildren() { + return 9; + } + protected void saveImpl2(CompoundTag tag) { } protected void loadImpl2(CompoundTag tag) { } + + protected abstract Status updateImpl2(IGolem golem); } diff --git a/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java b/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java index 6a71aaa..1f310db 100644 --- a/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java +++ b/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java @@ -14,6 +14,11 @@ public abstract class ParameterRune extends Rune { super(type); } + public ParameterRune(RuneType> type, T data) { + this(type); + this.data = data; + } + public record UseContext(Level level, Player player, InteractionHand hand) { } diff --git a/src/main/java/info/shylie/ashes/api/rune/Rune.java b/src/main/java/info/shylie/ashes/api/rune/Rune.java index 7047acd..8bd4339 100644 --- a/src/main/java/info/shylie/ashes/api/rune/Rune.java +++ b/src/main/java/info/shylie/ashes/api/rune/Rune.java @@ -16,8 +16,8 @@ public abstract class Rune { this.type = type; } - public String getTypeID() { - return type.toString(); + public ResourceLocation getTypeID() { + return AshesRegistry.RUNE_REGISTRY.get().getKey(type); } public Component getTooltip() { diff --git a/src/main/java/info/shylie/ashes/api/rune/leaf/BEInteractRune.java b/src/main/java/info/shylie/ashes/api/rune/leaf/BEInteractRune.java new file mode 100644 index 0000000..2d0513b --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/leaf/BEInteractRune.java @@ -0,0 +1,31 @@ +package info.shylie.ashes.api.rune.leaf; + +import info.shylie.ashes.Utils; +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.LeafTaskRune; +import info.shylie.ashes.api.rune.RuneType; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; + +public abstract class BEInteractRune extends LeafTaskRune { + protected BEInteractRune(RuneType type, Class... extra) { + super(type, Utils.concat(extra, BlockPos.class)); + } + + @Override + protected boolean startImpl(IGolem golem) { + return golem.canInteract(getValue(0)); + } + + @Override + protected Status updateImpl(IGolem golem) { + BlockEntity be = golem.asEntity().level().getBlockEntity(getValue(0)); + if (be != null) { + return interact(golem, be); + } + + return Status.SUCCESS; + } + + protected abstract Status interact(IGolem golem, BlockEntity be); +} diff --git a/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java b/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java index d80e11f..f24f1d3 100644 --- a/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java +++ b/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java @@ -64,11 +64,13 @@ public class AshenGolemEntity extends AbstractGolem implements IGolem { @Override public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); if (task != null) { task.save(tag); } } @Override public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); task = Rune.of(tag, TaskRune.class); } diff --git a/src/main/java/info/shylie/ashes/item/RuneItem.java b/src/main/java/info/shylie/ashes/item/RuneItem.java index f684ec1..242dbce 100644 --- a/src/main/java/info/shylie/ashes/item/RuneItem.java +++ b/src/main/java/info/shylie/ashes/item/RuneItem.java @@ -2,6 +2,7 @@ package info.shylie.ashes.item; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.*; +import net.minecraft.Util; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -102,6 +103,15 @@ public class RuneItem extends Item { components.add(component); } + @Override + public String getDescriptionId(ItemStack stack) { + Rune rune = Rune.of(stack.getTag()); + if (rune == null) { + return super.getDescriptionId(stack); + } + return Util.makeDescriptionId("rune", rune.getTypeID()); + } + private boolean setParameter(UseOnContext context) { ParameterRune rune = Rune.of(context.getItemInHand().getTag(), ParameterRune.class); if (rune == null) { return false; } diff --git a/src/main/java/info/shylie/ashes/rune/param/ItemRune.java b/src/main/java/info/shylie/ashes/rune/param/ItemRune.java index a642e65..4031b72 100644 --- a/src/main/java/info/shylie/ashes/rune/param/ItemRune.java +++ b/src/main/java/info/shylie/ashes/rune/param/ItemRune.java @@ -7,19 +7,20 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; import net.minecraftforge.registries.ForgeRegistries; public class ItemRune extends ParameterRune { private static final String ITEM_TAG = "item"; public ItemRune(RuneType type) { - super(type); + super(type, Items.AIR); } @Override public TooltipComponent getTooltipImage() { Item item = getValue(); - if (item == null) { return null; } + if (item == Items.AIR) { return null; } return new SingleItemTooltip(item); } diff --git a/src/main/java/info/shylie/ashes/rune/task/composite/InverterRune.java b/src/main/java/info/shylie/ashes/rune/task/composite/InverterRune.java new file mode 100644 index 0000000..9ba6a74 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/composite/InverterRune.java @@ -0,0 +1,22 @@ +package info.shylie.ashes.rune.task.composite; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.CompositeTaskRune; +import info.shylie.ashes.api.rune.RuneType; + +public class InverterRune extends CompositeTaskRune { + public InverterRune(RuneType type) { + super(type); + } + + @Override + protected Status updateImpl2(IGolem golem) { + Status status = children.get(0).update(golem); + + return switch (status) { + case SUCCESS -> Status.FAILURE; + case FAILURE -> Status.SUCCESS; + default -> Status.ONGOING; + }; + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/SelectorRune.java b/src/main/java/info/shylie/ashes/rune/task/composite/SelectorRune.java similarity index 93% rename from src/main/java/info/shylie/ashes/rune/task/SelectorRune.java rename to src/main/java/info/shylie/ashes/rune/task/composite/SelectorRune.java index 43d3c54..6262f57 100644 --- a/src/main/java/info/shylie/ashes/rune/task/SelectorRune.java +++ b/src/main/java/info/shylie/ashes/rune/task/composite/SelectorRune.java @@ -1,4 +1,4 @@ -package info.shylie.ashes.rune.task; +package info.shylie.ashes.rune.task.composite; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.CompositeTaskRune; diff --git a/src/main/java/info/shylie/ashes/rune/task/SequenceRune.java b/src/main/java/info/shylie/ashes/rune/task/composite/SequenceRune.java similarity index 93% rename from src/main/java/info/shylie/ashes/rune/task/SequenceRune.java rename to src/main/java/info/shylie/ashes/rune/task/composite/SequenceRune.java index 42df5c1..1937d2d 100644 --- a/src/main/java/info/shylie/ashes/rune/task/SequenceRune.java +++ b/src/main/java/info/shylie/ashes/rune/task/composite/SequenceRune.java @@ -1,4 +1,4 @@ -package info.shylie.ashes.rune.task; +package info.shylie.ashes.rune.task.composite; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.RuneType; diff --git a/src/main/java/info/shylie/ashes/rune/task/StepSelectorRune.java b/src/main/java/info/shylie/ashes/rune/task/composite/StepSelectorRune.java similarity index 94% rename from src/main/java/info/shylie/ashes/rune/task/StepSelectorRune.java rename to src/main/java/info/shylie/ashes/rune/task/composite/StepSelectorRune.java index 3c28d90..68662c3 100644 --- a/src/main/java/info/shylie/ashes/rune/task/StepSelectorRune.java +++ b/src/main/java/info/shylie/ashes/rune/task/composite/StepSelectorRune.java @@ -1,4 +1,4 @@ -package info.shylie.ashes.rune.task; +package info.shylie.ashes.rune.task.composite; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.CompositeTaskRune; diff --git a/src/main/java/info/shylie/ashes/rune/task/StepSequenceRune.java b/src/main/java/info/shylie/ashes/rune/task/composite/StepSequenceRune.java similarity index 94% rename from src/main/java/info/shylie/ashes/rune/task/StepSequenceRune.java rename to src/main/java/info/shylie/ashes/rune/task/composite/StepSequenceRune.java index 1c74d10..c26d1f7 100644 --- a/src/main/java/info/shylie/ashes/rune/task/StepSequenceRune.java +++ b/src/main/java/info/shylie/ashes/rune/task/composite/StepSequenceRune.java @@ -1,4 +1,4 @@ -package info.shylie.ashes.rune.task; +package info.shylie.ashes.rune.task.composite; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.CompositeTaskRune; diff --git a/src/main/java/info/shylie/ashes/rune/task/leaf/HasItemRune.java b/src/main/java/info/shylie/ashes/rune/task/leaf/HasItemRune.java new file mode 100644 index 0000000..fc1d62e --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/leaf/HasItemRune.java @@ -0,0 +1,17 @@ +package info.shylie.ashes.rune.task.leaf; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.LeafTaskRune; +import info.shylie.ashes.api.rune.RuneType; +import net.minecraft.world.item.Item; + +public class HasItemRune extends LeafTaskRune { + public HasItemRune(RuneType type) { + super(type, Item.class); + } + + @Override + protected Status updateImpl(IGolem golem) { + return golem.asEntity().getMainHandItem().getItem() == getValue(0) ? Status.SUCCESS : Status.FAILURE; + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/leaf/InteractRune.java b/src/main/java/info/shylie/ashes/rune/task/leaf/InteractRune.java new file mode 100644 index 0000000..1946004 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/leaf/InteractRune.java @@ -0,0 +1,42 @@ +package info.shylie.ashes.rune.task.leaf; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.LeafTaskRune; +import info.shylie.ashes.api.rune.RuneType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.util.FakePlayer; + +public class InteractRune extends LeafTaskRune { + public InteractRune(RuneType type) { + super(type, BlockPos.class); + } + + @Override + protected Status updateImpl(IGolem golem) { + BlockPos pos = getValue(0); + if (golem.canInteract(pos)) { + FakePlayer player = golem.getFakePlayer(); + player.setItemInHand(InteractionHand.MAIN_HAND, golem.asEntity().getMainHandItem()); + golem.asEntity().level().getBlockState(pos).use( + golem.asEntity().level(), + player, + InteractionHand.MAIN_HAND, + new BlockHitResult( + Vec3.atCenterOf(pos), + Direction.UP, + pos, + false + ) + ); + golem.asEntity().setItemInHand(InteractionHand.MAIN_HAND, player.getMainHandItem()); + + return Status.SUCCESS; + } + + return Status.FAILURE; + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/MoveRune.java b/src/main/java/info/shylie/ashes/rune/task/leaf/MoveRune.java similarity index 81% rename from src/main/java/info/shylie/ashes/rune/task/MoveRune.java rename to src/main/java/info/shylie/ashes/rune/task/leaf/MoveRune.java index 0351f35..184396c 100644 --- a/src/main/java/info/shylie/ashes/rune/task/MoveRune.java +++ b/src/main/java/info/shylie/ashes/rune/task/leaf/MoveRune.java @@ -1,4 +1,4 @@ -package info.shylie.ashes.rune.task; +package info.shylie.ashes.rune.task.leaf; import info.shylie.ashes.api.golem.IGolem; import info.shylie.ashes.api.rune.LeafTaskRune; @@ -36,22 +36,22 @@ public class MoveRune extends LeafTaskRune { e.move(MoverType.SELF, e.getDeltaMovement()); if (e.getNavigation().isDone()) { - BlockPos pos = getValue(0); - if (e.position().distanceToSqr(new Vec3(pos.getX(), pos.getY() + 1, pos.getZ())) < 1) { - return Status.SUCCESS; - } - - return Status.FAILURE; + return golem.canInteract(getValue(0)) ? Status.SUCCESS : Status.FAILURE; } return Status.ONGOING; } + @Override + protected void stopImpl(IGolem golem) { + golem.asEntity().getNavigation().stop(); + } + private boolean path(IGolem golem) { BlockPos pos = getValue(0); return pos != null && golem.asEntity().getNavigation().moveTo( pos.getX(), - pos.getY(), + pos.getY() + 1, pos.getZ(), 1.0 ); diff --git a/src/main/java/info/shylie/ashes/rune/task/leaf/PutItemRune.java b/src/main/java/info/shylie/ashes/rune/task/leaf/PutItemRune.java new file mode 100644 index 0000000..06bc0be --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/leaf/PutItemRune.java @@ -0,0 +1,43 @@ +package info.shylie.ashes.rune.task.leaf; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.RuneType; +import info.shylie.ashes.api.rune.leaf.BEInteractRune; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.ForgeCapabilities; + +public class PutItemRune extends BEInteractRune { + public PutItemRune(RuneType type) { + super(type); + } + + @Override + protected Status interact(IGolem golem, BlockEntity be) { + ItemStack handItem = golem.asEntity().getMainHandItem().copy(); + + if (handItem.isEmpty()) { + return Status.FAILURE; + } + + final Status[] status = { Status.FAILURE }; + + be.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(handler -> { + for (int i = 0; i < handler.getSlots() && !handItem.isEmpty(); i++) { + int putCount = handItem.getCount() - handler.insertItem(i, handItem, true).getCount(); + if (putCount > 0) { + handler.insertItem(i, handItem.copy(), false); + handItem.shrink(putCount); + status[0] = Status.SUCCESS; + } + } + }); + + if (status[0] == Status.SUCCESS) { + golem.asEntity().setItemInHand(InteractionHand.MAIN_HAND, handItem); + } + + return status[0]; + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/leaf/TakeItemRune.java b/src/main/java/info/shylie/ashes/rune/task/leaf/TakeItemRune.java new file mode 100644 index 0000000..4ab3bc3 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/leaf/TakeItemRune.java @@ -0,0 +1,57 @@ +package info.shylie.ashes.rune.task.leaf; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.RuneType; +import info.shylie.ashes.api.rune.leaf.BEInteractRune; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.items.ItemHandlerHelper; + +public class TakeItemRune extends BEInteractRune { + public TakeItemRune(RuneType type) { + super(type, Item.class); + } + + @Override + protected Status interact(IGolem golem, BlockEntity be) { + final Status[] status = { Status.FAILURE }; + + be.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(handler -> { + Item type = getValue(1); + ItemStack totalExtracted = golem.asEntity().getMainHandItem().copy(); + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack stack = handler.getStackInSlot(i); + + if (!totalExtracted.isEmpty() && !ItemHandlerHelper.canItemStacksStack(totalExtracted, stack)) { + continue; + } + + if (type == null || stack.getItem() == type) { + int count = Math.min(stack.getCount(), stack.getMaxStackSize()); + if (!totalExtracted.isEmpty()) { + count = Math.min(totalExtracted.getMaxStackSize() - totalExtracted.getCount(), count); + } + ItemStack extracted = handler.extractItem(i, count, false); + if (!extracted.isEmpty()) { + if (totalExtracted.isEmpty()) { + totalExtracted = extracted; + } + else { + totalExtracted.grow(extracted.getCount()); + } + status[0] = Status.SUCCESS; + } + } + } + + if (status[0] == Status.SUCCESS) { + golem.asEntity().setItemInHand(InteractionHand.MAIN_HAND, totalExtracted); + } + }); + + return status[0]; + } +} diff --git a/src/main/resources/assets/ashes/lang/en_us.json b/src/main/resources/assets/ashes/lang/en_us.json index ae839bb..1c79248 100644 --- a/src/main/resources/assets/ashes/lang/en_us.json +++ b/src/main/resources/assets/ashes/lang/en_us.json @@ -1,7 +1,20 @@ { + "rune.ashes.blockpos": "Position Rune", + "rune.ashes.item": "Item Rune", + "rune.ashes.sequence": "Sequence Rune", + "rune.ashes.selector": "Selector Rune", + "rune.ashes.step_sequence": "Step Sequence Rune", + "rune.ashes.step_selector": "Step Selector Rune", + "rune.ashes.inverter":"Inverter Rune", + "rune.ashes.move": "Move Rune", + "rune.ashes.take_item": "Take Item Rune", + "rune.ashes.put_item": "Put Item Rune", + "rune.ashes.interact": "Interact Rune", + "rune.ashes.has_item": "Has Item Rune", + "rune.ashes.blockpos.default": "No position set", - "item.ashes.rune": "Rune", + "item.ashes.rune": "Invalid Rune", "itemgroup.ashes": "Ashes" } \ No newline at end of file