diff --git a/src/generated/resources/.cache/81865ef998211ff68340afa8e6cd0e1cf8ec012a b/src/generated/resources/.cache/81865ef998211ff68340afa8e6cd0e1cf8ec012a index 126e14f..a8c7f22 100644 --- a/src/generated/resources/.cache/81865ef998211ff68340afa8e6cd0e1cf8ec012a +++ b/src/generated/resources/.cache/81865ef998211ff68340afa8e6cd0e1cf8ec012a @@ -1,4 +1,4 @@ -// 1.20.1 2024-07-30T13:47:57.4562926 Item Models: ashes +// 1.20.1 2024-08-02T01:31:53.2565269 Item Models: ashes 63ec6c618a3a23eab4cab9c52d7d3250de9b516e assets/ashes/models/item/ashen_golem_spawn_egg.json 060aa5df9f6e6c2d7476baa7c80b996a3c9fd4df assets/ashes/models/item/heated_ashy_ingot.json -49ecc679012241ea5a31c76f6ffde9236c2a2ef5 assets/ashes/models/item/param_rune.json +49ecc679012241ea5a31c76f6ffde9236c2a2ef5 assets/ashes/models/item/rune.json diff --git a/src/generated/resources/assets/ashes/models/item/param_rune.json b/src/generated/resources/assets/ashes/models/item/rune.json similarity index 100% rename from src/generated/resources/assets/ashes/models/item/param_rune.json rename to src/generated/resources/assets/ashes/models/item/rune.json diff --git a/src/main/java/info/shylie/ashes/Ashes.java b/src/main/java/info/shylie/ashes/Ashes.java index 51c36be..c1ff824 100644 --- a/src/main/java/info/shylie/ashes/Ashes.java +++ b/src/main/java/info/shylie/ashes/Ashes.java @@ -1,6 +1,7 @@ package info.shylie.ashes; import com.mojang.logging.LogUtils; +import info.shylie.ashes.api.tooltip.SingleItemTooltip; import info.shylie.ashes.datagen.ItemModels; import info.shylie.ashes.entity.AshenGolemEntity; import info.shylie.ashes.entity.render.AshenGolemRenderer; @@ -9,6 +10,7 @@ import net.minecraft.data.PackOutput; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.EntityRenderersEvent; +import net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.data.event.GatherDataEvent; @@ -19,7 +21,6 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.slf4j.Logger; @@ -30,8 +31,7 @@ public class Ashes public static final String MODID = "ashes"; public static final Logger LOGGER = LogUtils.getLogger(); - public Ashes() - { + public Ashes() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); modEventBus.addListener(this::commonSetup); @@ -42,16 +42,14 @@ public class Ashes AshesRegistry.ITEMS.register(modEventBus); AshesRegistry.ENTITY_TYPES.register(modEventBus); AshesRegistry.CREATIVE_TABS.register(modEventBus); - AshesRegistry.TASK_RUNES.register(modEventBus); - AshesRegistry.PARAM_RUNES.register(modEventBus); + AshesRegistry.RUNES.register(modEventBus); MinecraftForge.EVENT_BUS.register(this); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC); } - private void commonSetup(final FMLCommonSetupEvent event) - { + private void commonSetup(final FMLCommonSetupEvent event) { LOGGER.info("From embers to ashes..."); } @@ -68,16 +66,15 @@ public class Ashes } @SubscribeEvent - public void onServerStarting(ServerStartingEvent event) - { + public void serverStarting(ServerStartingEvent event) { } @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public static class ClientModEvents { @OnlyIn(Dist.CLIENT) @SubscribeEvent - public static void onClientSetup(FMLClientSetupEvent event) - { + public static void registerTooltipComponents(RegisterClientTooltipComponentFactoriesEvent event) { + event.register(SingleItemTooltip.class, SingleItemTooltip.SingleItemClientTooltip::new); } @OnlyIn(Dist.CLIENT) diff --git a/src/main/java/info/shylie/ashes/AshesRegistry.java b/src/main/java/info/shylie/ashes/AshesRegistry.java index fd6de6e..1451f45 100644 --- a/src/main/java/info/shylie/ashes/AshesRegistry.java +++ b/src/main/java/info/shylie/ashes/AshesRegistry.java @@ -2,11 +2,15 @@ package info.shylie.ashes; import com.rekindled.embers.RegistryManager; import com.rekindled.embers.util.Misc; -import info.shylie.ashes.api.rune.IParamRune; -import info.shylie.ashes.api.rune.ITaskRune; +import info.shylie.ashes.api.rune.Rune; +import info.shylie.ashes.api.rune.RuneType; import info.shylie.ashes.entity.AshenGolemEntity; -import info.shylie.ashes.item.ParamRune; +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.MoveRune; +import info.shylie.ashes.rune.task.SelectorRune; +import info.shylie.ashes.rune.task.SequenceRune; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -28,30 +32,33 @@ public class AshesRegistry { public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Ashes.MODID); public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, Ashes.MODID); public static final DeferredRegister CREATIVE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, Ashes.MODID); - public static final DeferredRegister TASK_RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "task_runes"), Ashes.MODID); - public static final DeferredRegister> PARAM_RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "param_runes"), Ashes.MODID); + public static final DeferredRegister> RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "rune"), Ashes.MODID); - public static final Supplier> TASK_RUNES_REGISTRY = TASK_RUNES.makeRegistry(RegistryBuilder::new); - public static final Supplier>> PARAM_RUNES_REGISTRY = PARAM_RUNES.makeRegistry(RegistryBuilder::new); + public static final Supplier>> RUNE_REGISTRY = RUNES.makeRegistry(RegistryBuilder::new); public static final RegistryObject HEATED_ASHY_INGOT; - public static final RegistryObject PARAM_RUNE; + public static final RegistryObject RUNE; public static final RegistryObject ASHEN_GOLEM_SPAWN_EGG; public static final RegistryObject> ASHEN_GOLEM; public static final RegistryObject ASHES_TAB; - public static final RegistryObject BLOCK_POS_RUNE; + public static final RegistryObject> BLOCK_POS_RUNE; + public static final RegistryObject> ITEM_RUNE; + + public static final RegistryObject> SELECTOR_RUNE; + public static final RegistryObject> SEQUENCE_RUNE; + public static final RegistryObject> MOVE_RUNE; static { HEATED_ASHY_INGOT = ITEMS.register( "heated_ashy_ingot", () -> new Item(new Item.Properties()) ); - PARAM_RUNE = ITEMS.register( - "param_rune", - () -> new ParamRune(new Item.Properties()) + RUNE = ITEMS.register( + "rune", + () -> new RuneItem(new Item.Properties()) ); ASHEN_GOLEM = RegistryManager.registerEntity( @@ -74,7 +81,12 @@ public class AshesRegistry { }).build() ); - BLOCK_POS_RUNE = PARAM_RUNES.register("block_pos_rune", BlockPosRune::new); + BLOCK_POS_RUNE = RUNES.register("blockpos", RuneType.Builder.of(BlockPosRune::new)::build); + ITEM_RUNE = RUNES.register("item", RuneType.Builder.of(ItemRune::new)::build); + + SEQUENCE_RUNE = RUNES.register("sequence", RuneType.Builder.of(SequenceRune::new)::build); + SELECTOR_RUNE = RUNES.register("selector", RuneType.Builder.of(SelectorRune::new)::build); + MOVE_RUNE = RUNES.register("move", RuneType.Builder.of(MoveRune::new)::build); } public static RegistryObject registerSpawnEgg(RegistryObject> type, int bg, int fg) { diff --git a/src/main/java/info/shylie/ashes/api/golem/IGolem.java b/src/main/java/info/shylie/ashes/api/golem/IGolem.java new file mode 100644 index 0000000..29bc461 --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/golem/IGolem.java @@ -0,0 +1,19 @@ +package info.shylie.ashes.api.golem; + +import info.shylie.ashes.api.rune.TaskRune; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.animal.AbstractGolem; + +public interface IGolem { + AbstractGolem asEntity(); + + TaskRune getTask(); + void setTask(TaskRune task); + + default void updateTask() { + TaskRune task = getTask(); + if (task != null) { + task.update(this); + } + } +} diff --git a/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java b/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java new file mode 100644 index 0000000..8d74588 --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/CompositeTaskRune.java @@ -0,0 +1,76 @@ +package info.shylie.ashes.api.rune; + +import info.shylie.ashes.api.golem.IGolem; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +import java.util.function.Function; + +public abstract class CompositeTaskRune extends TaskRune { + public static final String CHILDREN_TAG = "children"; + + protected final NonNullList children; + + public CompositeTaskRune(RuneType type) { + super(type); + children = NonNullList.create(); + } + + public void add(TaskRune rune) { + if (rune == null) { return; } + children.add(rune); + } + + public void remove(TaskRune rune) { + children.remove(rune); + } + + public void remove(Function filter) { + children.removeIf(filter::apply); + } + + @Override + protected boolean startImpl(IGolem golem) { + return !children.isEmpty(); + } + + @Override + protected final Status updateImpl(IGolem golem) { + for (TaskRune child : children) { + child.ran = false; + } + + Status status = updateImpl2(golem); + + for (TaskRune child : children) { + if (!child.ran) { + child.stop(golem); + } + } + + return status; + } + + protected abstract Status updateImpl2(IGolem golem); + + @Override + protected final void saveImpl(CompoundTag tag) { + ListTag listTag = new ListTag(); + for (TaskRune child : children) { + CompoundTag childTag = new CompoundTag(); + child.save(childTag); + listTag.add(childTag); + } + tag.put(CHILDREN_TAG, listTag); + } + + @Override + protected final void loadImpl(CompoundTag tag) { + ListTag list = tag.getList(CHILDREN_TAG, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + add(Rune.of(list.getCompound(i), TaskRune.class)); + } + } +} diff --git a/src/main/java/info/shylie/ashes/api/rune/IParamRune.java b/src/main/java/info/shylie/ashes/api/rune/IParamRune.java deleted file mode 100644 index 49dc7bf..0000000 --- a/src/main/java/info/shylie/ashes/api/rune/IParamRune.java +++ /dev/null @@ -1,29 +0,0 @@ -package info.shylie.ashes.api.rune; - -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.Level; - -public interface IParamRune extends IRune { - T getParameter(ItemStack stack); - - default boolean setParameter(UseOnContext context) { - return false; - } - - default boolean setParameter(UseContext context) { - return false; - } - - default boolean setParameter(ItemStackedOnMeContext context) { - return false; - } - - record UseContext(Level level, Player player, InteractionHand hand) { - } - - record ItemStackedOnMeContext(ItemStack me, ItemStack other) { - } -} diff --git a/src/main/java/info/shylie/ashes/api/rune/IRune.java b/src/main/java/info/shylie/ashes/api/rune/IRune.java deleted file mode 100644 index 3f4c550..0000000 --- a/src/main/java/info/shylie/ashes/api/rune/IRune.java +++ /dev/null @@ -1,7 +0,0 @@ -package info.shylie.ashes.api.rune; - -import net.minecraft.world.item.ItemStack; - -public interface IRune { - String getDescription(ItemStack stack); -} diff --git a/src/main/java/info/shylie/ashes/api/rune/ITaskRune.java b/src/main/java/info/shylie/ashes/api/rune/ITaskRune.java deleted file mode 100644 index 2467728..0000000 --- a/src/main/java/info/shylie/ashes/api/rune/ITaskRune.java +++ /dev/null @@ -1,4 +0,0 @@ -package info.shylie.ashes.api.rune; - -public interface ITaskRune extends IRune { -} diff --git a/src/main/java/info/shylie/ashes/api/rune/LeafTaskRune.java b/src/main/java/info/shylie/ashes/api/rune/LeafTaskRune.java new file mode 100644 index 0000000..1cb23dc --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/LeafTaskRune.java @@ -0,0 +1,54 @@ +package info.shylie.ashes.api.rune; + +import info.shylie.ashes.Ashes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +public abstract class LeafTaskRune extends TaskRune { + public static final String PARAMETERS_TAG = "parameters"; + + private final Class[] valid; + private final ParameterRune[] parameters; + + protected LeafTaskRune(RuneType type, Class... valid) { + super(type); + this.valid = valid; + parameters = new ParameterRune[valid.length]; + } + + protected final T getValue(int index) { + if (index < 0 || index >= parameters.length) { return null; } + if (parameters[index] == null) { return null; } + return (T)parameters[index].getValue(); + } + + public void setParameter(int index, ParameterRune parameter) { + if (index < 0 || index >= parameters.length) { return; } + if (parameter == null || valid[index].isInstance(parameter.getValue())) { + parameters[index] = parameter; + } + else { + Ashes.LOGGER.warn("Invalid parameter type '{}' provided to leaf of type '{}'", parameter.getTypeID(), getTypeID()); + } + } + + @Override + protected final void saveImpl(CompoundTag tag) { + ListTag listTag = new ListTag(); + for (ParameterRune rune : parameters) { + CompoundTag runeTag = new CompoundTag(); + if (rune != null) { rune.save(runeTag); } + listTag.add(runeTag); + } + tag.put(PARAMETERS_TAG, listTag); + } + + @Override + protected final void loadImpl(CompoundTag tag) { + ListTag list = tag.getList(PARAMETERS_TAG, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + setParameter(i, Rune.of(list.getCompound(i), ParameterRune.class)); + } + } +} diff --git a/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java b/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java new file mode 100644 index 0000000..6a71aaa --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/ParameterRune.java @@ -0,0 +1,64 @@ +package info.shylie.ashes.api.rune; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; + +public abstract class ParameterRune extends Rune { + private T data; + + public ParameterRune(RuneType> type) { + super(type); + } + + public record UseContext(Level level, Player player, InteractionHand hand) { + } + + public record ItemStackedOnMeContext(ItemStack me, ItemStack other) { + } + + public final boolean setParameter(UseOnContext context) { + T newData = calculate(context); + + if (newData == null) { return false; } + + data = newData; + save(context.getItemInHand().getOrCreateTag()); + return true; + } + + public final boolean setParameter(UseContext context) { + T newData = calculate(context); + + if (newData == null) { return false; } + + data = newData; + save(context.player().getItemInHand(context.hand()).getOrCreateTag()); + return true; + } + + public final boolean setParameter(ItemStackedOnMeContext context) { + T newData = calculate(context); + + if (newData == null) { return false; } + + data = newData; + save(context.me().getOrCreateTag()); + return true; + } + + public final T getValue() { return data; } + + @Override + protected final void loadImpl(CompoundTag tag) { + data = loadImpl2(tag); + } + + protected T calculate(UseContext context) { return null; } + protected T calculate(UseOnContext context) { return null; } + protected T calculate(ItemStackedOnMeContext context) { return null; } + protected abstract T loadImpl2(CompoundTag tag); +} diff --git a/src/main/java/info/shylie/ashes/api/rune/Rune.java b/src/main/java/info/shylie/ashes/api/rune/Rune.java new file mode 100644 index 0000000..b5e2c71 --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/Rune.java @@ -0,0 +1,63 @@ +package info.shylie.ashes.api.rune; + +import info.shylie.ashes.AshesRegistry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.tooltip.TooltipComponent; + +public abstract class Rune { + public static final String RUNE_TYPE_TAG = "rune_type"; + public static final String RUNE_DATA_TAG = "rune_data"; + + private final RuneType type; + + public Rune(RuneType type) { + this.type = type; + } + + public String getTypeID() { + return type.toString(); + } + + public Component getTooltip() { + return Component.empty(); + } + + public TooltipComponent getTooltipImage() { + return null; + } + + public void save(CompoundTag tag) { + tag.putString(RUNE_TYPE_TAG, AshesRegistry.RUNE_REGISTRY.get().getKey(type).toString()); + CompoundTag runeData = new CompoundTag(); + saveImpl(runeData); + tag.put(RUNE_DATA_TAG, runeData); + } + + Rune load(CompoundTag tag) { + loadImpl(tag.getCompound(RUNE_DATA_TAG)); + return this; + } + + protected abstract void saveImpl(CompoundTag tag); + protected abstract void loadImpl(CompoundTag tag); + + public static Rune of(CompoundTag tag) { + return of(tag, Rune.class); + } + + public static T of(CompoundTag tag, Class clazz) { + if (tag == null) { return null; } + ResourceLocation location = ResourceLocation.tryParse(tag.getString(RUNE_TYPE_TAG)); + if (location == null) { return null; } + + RuneType type = AshesRegistry.RUNE_REGISTRY.get().getValue(location); + if (type == null) { return null; } + + Rune rune = type.create(); + if (!clazz.isInstance(rune)) { return null; } + + return (T)rune.load(tag); + } +} diff --git a/src/main/java/info/shylie/ashes/api/rune/RuneType.java b/src/main/java/info/shylie/ashes/api/rune/RuneType.java new file mode 100644 index 0000000..f8fead0 --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/RuneType.java @@ -0,0 +1,40 @@ +package info.shylie.ashes.api.rune; + +import info.shylie.ashes.AshesRegistry; + +public class RuneType { + private final RuneFactory factory; + + protected RuneType(RuneFactory factory) { + this.factory = factory; + } + + public T create() { + return factory.create(this); + } + + @Override + public String toString() { + return AshesRegistry.RUNE_REGISTRY.get().getKey(this).toString(); + } + + public interface RuneFactory { + T create(RuneType type); + } + + public static class Builder { + private final RuneFactory factory; + + private Builder(RuneFactory factory) { + this.factory = factory; + } + + public RuneType build() { + return new RuneType<>(factory); + } + + public static Builder of(RuneFactory factory) { + return new Builder<>(factory); + } + } +} diff --git a/src/main/java/info/shylie/ashes/api/rune/TaskRune.java b/src/main/java/info/shylie/ashes/api/rune/TaskRune.java new file mode 100644 index 0000000..e28c281 --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/rune/TaskRune.java @@ -0,0 +1,51 @@ +package info.shylie.ashes.api.rune; + +import info.shylie.ashes.api.golem.IGolem; + +public abstract class TaskRune extends Rune { + boolean ran; + private boolean running; + + public TaskRune(RuneType type) { + super(type); + } + + public enum Status { + SUCCESS, + FAILURE, + ONGOING + } + + private boolean start(IGolem golem) { + if (!running) { + running = startImpl(golem); + } + + return running; + } + + void stop(IGolem golem) { + if (running) { + stopImpl(golem); + + running = false; + } + } + + public final Status update(IGolem golem) { + if (!start(golem)) { + return Status.FAILURE; + } + ran = true; + Status status = updateImpl(golem); + if (status != Status.ONGOING) { + stop(golem); + } + + return status; + } + + protected boolean startImpl(IGolem golem) { return true; } + protected void stopImpl(IGolem golem) { } + protected abstract Status updateImpl(IGolem golem); +} diff --git a/src/main/java/info/shylie/ashes/api/tooltip/SingleItemTooltip.java b/src/main/java/info/shylie/ashes/api/tooltip/SingleItemTooltip.java new file mode 100644 index 0000000..90a8faa --- /dev/null +++ b/src/main/java/info/shylie/ashes/api/tooltip/SingleItemTooltip.java @@ -0,0 +1,39 @@ +package info.shylie.ashes.api.tooltip; + +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +public class SingleItemTooltip implements TooltipComponent { + private final Item item; + + public SingleItemTooltip(Item item) { + this.item = item; + } + + public static class SingleItemClientTooltip implements ClientTooltipComponent { + SingleItemTooltip tooltip; + + public SingleItemClientTooltip(SingleItemTooltip tooltip) { + this.tooltip = tooltip; + } + + @Override + public int getHeight() { + return 18; + } + + @Override + public int getWidth(Font font) { + return 18; + } + + @Override + public void renderImage(Font font, int x, int y, GuiGraphics graphics) { + graphics.renderFakeItem(new ItemStack(tooltip.item), x, y); + } + } +} diff --git a/src/main/java/info/shylie/ashes/datagen/ItemModels.java b/src/main/java/info/shylie/ashes/datagen/ItemModels.java index af8f052..93df815 100644 --- a/src/main/java/info/shylie/ashes/datagen/ItemModels.java +++ b/src/main/java/info/shylie/ashes/datagen/ItemModels.java @@ -17,7 +17,7 @@ public class ItemModels extends ItemModelProvider { @Override protected void registerModels() { basicItem(AshesRegistry.HEATED_ASHY_INGOT.get()); - itemWithTexture(AshesRegistry.PARAM_RUNE, "rune"); + basicItem(AshesRegistry.RUNE.get()); spawnEgg(AshesRegistry.ASHEN_GOLEM_SPAWN_EGG); } diff --git a/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java b/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java index d0c9601..f94eb5f 100644 --- a/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java +++ b/src/main/java/info/shylie/ashes/entity/AshenGolemEntity.java @@ -1,7 +1,12 @@ package info.shylie.ashes.entity; import com.rekindled.embers.datagen.EmbersSounds; +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.Rune; +import info.shylie.ashes.api.rune.TaskRune; +import info.shylie.ashes.entity.ai.GolemTaskGoal; import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.EntityType; @@ -12,8 +17,10 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; -public class AshenGolemEntity extends AbstractGolem { - public AshenGolemEntity(EntityType entityType, Level level) { +public class AshenGolemEntity extends AbstractGolem implements IGolem { + private TaskRune task; + + public AshenGolemEntity(EntityType entityType, Level level) { super(entityType, level); } @@ -37,4 +44,34 @@ public class AshenGolemEntity extends AbstractGolem { super.playStepSound(pos, state); playSound(EmbersSounds.ANCIENT_GOLEM_STEP.get()); } + + @Override + public AshenGolemEntity asEntity() { + return this; + } + + @Override + public TaskRune getTask() { + return task; + } + + @Override + public void setTask(TaskRune task) { + this.task = task; + } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + task.save(tag); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + task = Rune.of(tag, TaskRune.class); + } + + @Override + protected void registerGoals() { + goalSelector.addGoal(1, new GolemTaskGoal(this)); + } } diff --git a/src/main/java/info/shylie/ashes/entity/ai/GolemTaskGoal.java b/src/main/java/info/shylie/ashes/entity/ai/GolemTaskGoal.java new file mode 100644 index 0000000..dc4098a --- /dev/null +++ b/src/main/java/info/shylie/ashes/entity/ai/GolemTaskGoal.java @@ -0,0 +1,33 @@ +package info.shylie.ashes.entity.ai; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.TaskRune; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.ai.goal.Goal; + +public class GolemTaskGoal extends Goal { + private final IGolem golem; + + public GolemTaskGoal(IGolem golem) { + this.golem = golem; + } + + public void setTask(TaskRune task) { + golem.setTask(task); + } + + @Override + public boolean canUse() { + return golem.getTask() != null; + } + + @Override + public boolean canContinueToUse() { + return canUse(); + } + + @Override + public void tick() { + golem.updateTask(); + } +} diff --git a/src/main/java/info/shylie/ashes/item/ParamRune.java b/src/main/java/info/shylie/ashes/item/ParamRune.java deleted file mode 100644 index 18fa09f..0000000 --- a/src/main/java/info/shylie/ashes/item/ParamRune.java +++ /dev/null @@ -1,84 +0,0 @@ -package info.shylie.ashes.item; - -import info.shylie.ashes.Ashes; -import info.shylie.ashes.AshesRegistry; -import info.shylie.ashes.api.rune.IParamRune; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.SlotAccess; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.ClickAction; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.Level; - -public class ParamRune extends Item { - public ParamRune(Properties properties) { - super(properties); - } - - public boolean setParameter(UseOnContext context) { - IParamRune rune = getRune(context.getItemInHand()); - if (rune == null) { return false; } - return rune.setParameter(context); - } - public boolean setParameter(IParamRune.UseContext context) { - IParamRune rune = getRune(context.player().getItemInHand(context.hand())); - if (rune == null) { return false; } - return rune.setParameter(context); - } - public boolean setParameter(IParamRune.ItemStackedOnMeContext context) { - IParamRune rune = getRune(context.me()); - if (rune == null) { return false; } - return rune.setParameter(context); - } - - @Override - public final InteractionResultHolder use(Level level, Player player, InteractionHand hand) { - if (player.isCrouching() && setParameter(new IParamRune.UseContext(level, player, hand))) { - return InteractionResultHolder.success(player.getItemInHand(hand)); - } - - return InteractionResultHolder.pass(player.getItemInHand(hand)); - } - - @Override - public final InteractionResult useOn(UseOnContext context) { - Player player = context.getPlayer(); - - if (player != null && player.isCrouching() && setParameter(context)) { - return InteractionResult.SUCCESS; - } - - return InteractionResult.PASS; - } - - @Override - public boolean overrideOtherStackedOnMe(ItemStack me, ItemStack other, Slot slot, ClickAction action, Player player, SlotAccess slotAccess) { - return action == ClickAction.SECONDARY && !other.isEmpty() && setParameter(new IParamRune.ItemStackedOnMeContext(me, other)); - } - - @Override - public String getDescriptionId(ItemStack stack) { - IParamRune rune = getRune(stack); - if (rune == null) { return ""; } - String desc = rune.getDescription(stack); - return desc == null ? "" : desc; - } - - private IParamRune getRune(ItemStack stack) { - if (stack.getTag() == null) { return null; } - IParamRune rune = AshesRegistry.PARAM_RUNES_REGISTRY.get() - .getValue(new ResourceLocation(stack.getTag().getString("rune_type"))); - - if (rune == null) { - Ashes.LOGGER.warn("Unknown rune type: '{}'", stack.getTag().getString("rune_type")); - } - - return rune; - } -} diff --git a/src/main/java/info/shylie/ashes/item/RuneItem.java b/src/main/java/info/shylie/ashes/item/RuneItem.java new file mode 100644 index 0000000..66ea2bb --- /dev/null +++ b/src/main/java/info/shylie/ashes/item/RuneItem.java @@ -0,0 +1,116 @@ +package info.shylie.ashes.item; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.*; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.SlotAccess; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ClickAction; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class RuneItem extends Item { + public RuneItem(Properties properties) { + super(properties.stacksTo(1)); + } + + @Override + public final InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + if (player.isCrouching() && setParameter(new ParameterRune.UseContext(level, player, hand))) { + return InteractionResultHolder.success(player.getItemInHand(hand)); + } + + return InteractionResultHolder.pass(player.getItemInHand(hand)); + } + + @Override + public final InteractionResult useOn(UseOnContext context) { + Player player = context.getPlayer(); + + if (player != null && player.isCrouching() && setParameter(context)) { + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + @Override + public boolean overrideOtherStackedOnMe(ItemStack me, ItemStack other, Slot slot, ClickAction action, Player player, SlotAccess slotAccess) { + if (action != ClickAction.SECONDARY) { return false; } + + Rune rune = Rune.of(me.getTag()); + if (rune instanceof ParameterRune pRune) { + return !other.isEmpty() && setParameter(new ParameterRune.ItemStackedOnMeContext(me, other), pRune); + } + else if (rune instanceof CompositeTaskRune tRune) { + tRune.add(Rune.of(other.getTag(), TaskRune.class)); + tRune.save(me.getOrCreateTag()); + return !other.isEmpty(); + } + else if (rune instanceof LeafTaskRune tRune) { + tRune.setParameter(0, Rune.of(other.getTag(), ParameterRune.class)); + tRune.save(me.getOrCreateTag()); + return !other.isEmpty(); + } + + return false; + } + + @Override + public InteractionResult interactLivingEntity(ItemStack stack, Player player, LivingEntity entity, InteractionHand hand) { + if (entity instanceof IGolem golem) { + golem.setTask(Rune.of(stack.getTag(), TaskRune.class)); + + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + @Override + public Optional getTooltipImage(ItemStack stack) { + Rune rune = Rune.of(stack.getTag()); + if (rune == null) { return Optional.empty(); } + + return Optional.ofNullable(rune.getTooltipImage()); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List components, TooltipFlag flags) { + Rune rune = Rune.of(stack.getTag()); + if (rune == null) { return; } + + Component component = rune.getTooltip(); + if (component == null) { return; } + + components.add(component); + } + + private boolean setParameter(UseOnContext context) { + ParameterRune rune = Rune.of(context.getItemInHand().getTag(), ParameterRune.class); + if (rune == null) { return false; } + return rune.setParameter(context); + } + private boolean setParameter(ParameterRune.UseContext context) { + ParameterRune rune = Rune.of(context.player().getItemInHand(context.hand()).getTag(), ParameterRune.class); + if (rune == null) { return false; } + return rune.setParameter(context); + } + private boolean setParameter(ParameterRune.ItemStackedOnMeContext context, ParameterRune rune) { + if (rune == null) { return false; } + return rune.setParameter(context); + } +} diff --git a/src/main/java/info/shylie/ashes/rune/param/BlockPosRune.java b/src/main/java/info/shylie/ashes/rune/param/BlockPosRune.java index d29e650..c59730e 100644 --- a/src/main/java/info/shylie/ashes/rune/param/BlockPosRune.java +++ b/src/main/java/info/shylie/ashes/rune/param/BlockPosRune.java @@ -1,34 +1,47 @@ package info.shylie.ashes.rune.param; -import info.shylie.ashes.api.rune.IParamRune; +import info.shylie.ashes.api.rune.ParameterRune; +import info.shylie.ashes.api.rune.RuneType; +import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.ItemStack; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.context.UseOnContext; -public class BlockPosRune implements IParamRune { - @Override - public String getDescription(ItemStack stack) { - BlockPos pos = getParameter(stack); - if (pos == null) { return null; } - return String.format("(%d, %d, %d)", pos.getX(), pos.getY(), pos.getZ()); +public class BlockPosRune extends ParameterRune { + private static final String X_TAG = "x"; + private static final String Y_TAG = "y"; + private static final String Z_TAG = "z"; + + public BlockPosRune(RuneType type) { + super(type); } @Override - public BlockPos getParameter(ItemStack stack) { - CompoundTag tag = stack.getOrCreateTag(); - if (!tag.contains("x") || !tag.contains("y") || !tag.contains("z")) { - return null; + public Component getTooltip() { + BlockPos pos = getValue(); + if (pos == null) { + return Component.translatable("rune.ashes.blockpos.default").withStyle(ChatFormatting.BLUE); } - return new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); + return Component.literal( + String.format("%d, %d, %d", pos.getX(), pos.getY(), pos.getZ()) + ).withStyle(ChatFormatting.BLUE); } @Override - public boolean setParameter(UseOnContext context) { - CompoundTag tag = context.getItemInHand().getOrCreateTag(); - tag.putInt("x", context.getClickedPos().getX()); - tag.putInt("y", context.getClickedPos().getY()); - tag.putInt("z", context.getClickedPos().getZ()); - return true; + protected void saveImpl(CompoundTag tag) { + tag.putInt(X_TAG, getValue().getX()); + tag.putInt(Y_TAG, getValue().getY()); + tag.putInt(Z_TAG, getValue().getZ()); + } + + @Override + protected BlockPos loadImpl2(CompoundTag tag) { + return new BlockPos(tag.getInt(X_TAG), tag.getInt(Y_TAG), tag.getInt(Z_TAG)); + } + + @Override + protected BlockPos calculate(UseOnContext context) { + return context.getClickedPos(); } } diff --git a/src/main/java/info/shylie/ashes/rune/param/ItemRune.java b/src/main/java/info/shylie/ashes/rune/param/ItemRune.java new file mode 100644 index 0000000..bf90fdc --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/param/ItemRune.java @@ -0,0 +1,41 @@ +package info.shylie.ashes.rune.param; + +import info.shylie.ashes.api.rune.ParameterRune; +import info.shylie.ashes.api.rune.RuneType; +import info.shylie.ashes.api.tooltip.SingleItemTooltip; +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.minecraftforge.registries.ForgeRegistries; + +public class ItemRune extends ParameterRune { + private static final String ITEM_TAG = "item"; + + public ItemRune(RuneType type) { + super(type); + } + + @Override + public TooltipComponent getTooltipImage() { + Item item = getValue(); + if (item == null) { return null; } + + return new SingleItemTooltip(item); + } + + @Override + protected Item calculate(ItemStackedOnMeContext context) { + return context.other().getItem(); + } + + @Override + protected Item loadImpl2(CompoundTag tag) { + return ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(tag.getString(ITEM_TAG))); + } + + @Override + protected void saveImpl(CompoundTag tag) { + tag.putString(ITEM_TAG, ForgeRegistries.ITEMS.getKey(getValue()).toString()); + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/MoveRune.java b/src/main/java/info/shylie/ashes/rune/task/MoveRune.java new file mode 100644 index 0000000..04e05e4 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/MoveRune.java @@ -0,0 +1,50 @@ +package info.shylie.ashes.rune.task; + +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.entity.MoverType; +import net.minecraft.world.entity.animal.AbstractGolem; +import net.minecraft.world.phys.Vec3; + +public class MoveRune extends LeafTaskRune { + public MoveRune(RuneType type) { + super(type, BlockPos.class); + } + + @Override + protected boolean startImpl(IGolem golem) { + return path(golem); + } + + @Override + protected Status updateImpl(IGolem golem) { + AbstractGolem e = golem.asEntity(); + + if (e.getNavigation().isStuck()) { + return Status.FAILURE; + } + + e.moveRelative(0.02F, + new Vec3( + e.xxa, + e.yya, + e.zza + ) + ); + e.move(MoverType.SELF, e.getDeltaMovement()); + + return e.getNavigation().isDone() ? Status.SUCCESS : Status.ONGOING; + } + + private boolean path(IGolem golem) { + BlockPos pos = getValue(0); + return pos != null && golem.asEntity().getNavigation().moveTo( + pos.getX(), + pos.getY(), + pos.getZ(), + 1.0 + ); + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/SelectorRune.java b/src/main/java/info/shylie/ashes/rune/task/SelectorRune.java new file mode 100644 index 0000000..43d3c54 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/SelectorRune.java @@ -0,0 +1,25 @@ +package info.shylie.ashes.rune.task; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.CompositeTaskRune; +import info.shylie.ashes.api.rune.RuneType; +import info.shylie.ashes.api.rune.TaskRune; + +public class SelectorRune extends CompositeTaskRune { + public SelectorRune(RuneType type) { + super(type); + } + + @Override + protected Status updateImpl2(IGolem golem) { + for (TaskRune rune : children) { + Status status = rune.update(golem); + + if (status != Status.FAILURE) { + return status; + } + } + + return Status.SUCCESS; + } +} diff --git a/src/main/java/info/shylie/ashes/rune/task/SequenceRune.java b/src/main/java/info/shylie/ashes/rune/task/SequenceRune.java new file mode 100644 index 0000000..42df5c1 --- /dev/null +++ b/src/main/java/info/shylie/ashes/rune/task/SequenceRune.java @@ -0,0 +1,25 @@ +package info.shylie.ashes.rune.task; + +import info.shylie.ashes.api.golem.IGolem; +import info.shylie.ashes.api.rune.RuneType; +import info.shylie.ashes.api.rune.TaskRune; +import info.shylie.ashes.api.rune.CompositeTaskRune; + +public class SequenceRune extends CompositeTaskRune { + public SequenceRune(RuneType type) { + super(type); + } + + @Override + protected Status updateImpl2(IGolem golem) { + for (TaskRune rune : children) { + Status status = rune.update(golem); + + if (status != Status.SUCCESS) { + return status; + } + } + + return Status.SUCCESS; + } +} diff --git a/src/main/resources/assets/ashes/lang/en_us.json b/src/main/resources/assets/ashes/lang/en_us.json new file mode 100644 index 0000000..ae839bb --- /dev/null +++ b/src/main/resources/assets/ashes/lang/en_us.json @@ -0,0 +1,7 @@ +{ + "rune.ashes.blockpos.default": "No position set", + + "item.ashes.rune": "Rune", + + "itemgroup.ashes": "Ashes" +} \ No newline at end of file