Refactor rune code

This commit is contained in:
shylie 2024-08-02 02:16:16 -04:00
parent 4fa9816848
commit 399026f903
26 changed files with 810 additions and 172 deletions

View File

@ -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 63ec6c618a3a23eab4cab9c52d7d3250de9b516e assets/ashes/models/item/ashen_golem_spawn_egg.json
060aa5df9f6e6c2d7476baa7c80b996a3c9fd4df assets/ashes/models/item/heated_ashy_ingot.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

View File

@ -1,6 +1,7 @@
package info.shylie.ashes; package info.shylie.ashes;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import info.shylie.ashes.api.tooltip.SingleItemTooltip;
import info.shylie.ashes.datagen.ItemModels; import info.shylie.ashes.datagen.ItemModels;
import info.shylie.ashes.entity.AshenGolemEntity; import info.shylie.ashes.entity.AshenGolemEntity;
import info.shylie.ashes.entity.render.AshenGolemRenderer; 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.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.data.event.GatherDataEvent; 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.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -30,8 +31,7 @@ public class Ashes
public static final String MODID = "ashes"; public static final String MODID = "ashes";
public static final Logger LOGGER = LogUtils.getLogger(); public static final Logger LOGGER = LogUtils.getLogger();
public Ashes() public Ashes() {
{
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(this::commonSetup); modEventBus.addListener(this::commonSetup);
@ -42,16 +42,14 @@ public class Ashes
AshesRegistry.ITEMS.register(modEventBus); AshesRegistry.ITEMS.register(modEventBus);
AshesRegistry.ENTITY_TYPES.register(modEventBus); AshesRegistry.ENTITY_TYPES.register(modEventBus);
AshesRegistry.CREATIVE_TABS.register(modEventBus); AshesRegistry.CREATIVE_TABS.register(modEventBus);
AshesRegistry.TASK_RUNES.register(modEventBus); AshesRegistry.RUNES.register(modEventBus);
AshesRegistry.PARAM_RUNES.register(modEventBus);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC); 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..."); LOGGER.info("From embers to ashes...");
} }
@ -68,16 +66,15 @@ public class Ashes
} }
@SubscribeEvent @SubscribeEvent
public void onServerStarting(ServerStartingEvent event) public void serverStarting(ServerStartingEvent event) {
{
} }
@Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public static class ClientModEvents { public static class ClientModEvents {
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
@SubscribeEvent @SubscribeEvent
public static void onClientSetup(FMLClientSetupEvent event) public static void registerTooltipComponents(RegisterClientTooltipComponentFactoriesEvent event) {
{ event.register(SingleItemTooltip.class, SingleItemTooltip.SingleItemClientTooltip::new);
} }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)

View File

@ -2,11 +2,15 @@ package info.shylie.ashes;
import com.rekindled.embers.RegistryManager; import com.rekindled.embers.RegistryManager;
import com.rekindled.embers.util.Misc; import com.rekindled.embers.util.Misc;
import info.shylie.ashes.api.rune.IParamRune; import info.shylie.ashes.api.rune.Rune;
import info.shylie.ashes.api.rune.ITaskRune; import info.shylie.ashes.api.rune.RuneType;
import info.shylie.ashes.entity.AshenGolemEntity; 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.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.core.registries.Registries;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -28,30 +32,33 @@ public class AshesRegistry {
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Ashes.MODID); public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Ashes.MODID);
public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, Ashes.MODID); public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, Ashes.MODID);
public static final DeferredRegister<CreativeModeTab> CREATIVE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, Ashes.MODID); public static final DeferredRegister<CreativeModeTab> CREATIVE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, Ashes.MODID);
public static final DeferredRegister<ITaskRune> TASK_RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "task_runes"), Ashes.MODID); public static final DeferredRegister<RuneType<? extends Rune>> RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "rune"), Ashes.MODID);
public static final DeferredRegister<IParamRune<?>> PARAM_RUNES = DeferredRegister.create(new ResourceLocation(Ashes.MODID, "param_runes"), Ashes.MODID);
public static final Supplier<IForgeRegistry<ITaskRune>> TASK_RUNES_REGISTRY = TASK_RUNES.makeRegistry(RegistryBuilder::new); public static final Supplier<IForgeRegistry<RuneType<?>>> RUNE_REGISTRY = RUNES.makeRegistry(RegistryBuilder::new);
public static final Supplier<IForgeRegistry<IParamRune<?>>> PARAM_RUNES_REGISTRY = PARAM_RUNES.makeRegistry(RegistryBuilder::new);
public static final RegistryObject<Item> HEATED_ASHY_INGOT; public static final RegistryObject<Item> HEATED_ASHY_INGOT;
public static final RegistryObject<ParamRune> PARAM_RUNE; public static final RegistryObject<RuneItem> RUNE;
public static final RegistryObject<ForgeSpawnEggItem> ASHEN_GOLEM_SPAWN_EGG; public static final RegistryObject<ForgeSpawnEggItem> ASHEN_GOLEM_SPAWN_EGG;
public static final RegistryObject<EntityType<AshenGolemEntity>> ASHEN_GOLEM; public static final RegistryObject<EntityType<AshenGolemEntity>> ASHEN_GOLEM;
public static final RegistryObject<CreativeModeTab> ASHES_TAB; public static final RegistryObject<CreativeModeTab> ASHES_TAB;
public static final RegistryObject<BlockPosRune> BLOCK_POS_RUNE; public static final RegistryObject<RuneType<BlockPosRune>> BLOCK_POS_RUNE;
public static final RegistryObject<RuneType<ItemRune>> ITEM_RUNE;
public static final RegistryObject<RuneType<SelectorRune>> SELECTOR_RUNE;
public static final RegistryObject<RuneType<SequenceRune>> SEQUENCE_RUNE;
public static final RegistryObject<RuneType<MoveRune>> MOVE_RUNE;
static { static {
HEATED_ASHY_INGOT = ITEMS.register( HEATED_ASHY_INGOT = ITEMS.register(
"heated_ashy_ingot", "heated_ashy_ingot",
() -> new Item(new Item.Properties()) () -> new Item(new Item.Properties())
); );
PARAM_RUNE = ITEMS.register( RUNE = ITEMS.register(
"param_rune", "rune",
() -> new ParamRune(new Item.Properties()) () -> new RuneItem(new Item.Properties())
); );
ASHEN_GOLEM = RegistryManager.registerEntity( ASHEN_GOLEM = RegistryManager.registerEntity(
@ -74,7 +81,12 @@ public class AshesRegistry {
}).build() }).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<ForgeSpawnEggItem> registerSpawnEgg(RegistryObject<? extends EntityType<? extends Mob>> type, int bg, int fg) { public static RegistryObject<ForgeSpawnEggItem> registerSpawnEgg(RegistryObject<? extends EntityType<? extends Mob>> type, int bg, int fg) {

View File

@ -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);
}
}
}

View File

@ -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<TaskRune> children;
public CompositeTaskRune(RuneType<? extends CompositeTaskRune> 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<TaskRune, Boolean> 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));
}
}
}

View File

@ -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<T> 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) {
}
}

View File

@ -1,7 +0,0 @@
package info.shylie.ashes.api.rune;
import net.minecraft.world.item.ItemStack;
public interface IRune {
String getDescription(ItemStack stack);
}

View File

@ -1,4 +0,0 @@
package info.shylie.ashes.api.rune;
public interface ITaskRune extends IRune {
}

View File

@ -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<? extends LeafTaskRune> type, Class<?>... valid) {
super(type);
this.valid = valid;
parameters = new ParameterRune<?>[valid.length];
}
protected final <T> T getValue(int index) {
if (index < 0 || index >= parameters.length) { return null; }
if (parameters[index] == null) { return null; }
return (T)parameters[index].getValue();
}
public <T> void setParameter(int index, ParameterRune<? extends T> 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));
}
}
}

View File

@ -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<T> extends Rune {
private T data;
public ParameterRune(RuneType<? extends ParameterRune<?>> 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);
}

View File

@ -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<? extends Rune> type;
public Rune(RuneType<? extends Rune> 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 extends Rune> T of(CompoundTag tag, Class<T> 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);
}
}

View File

@ -0,0 +1,40 @@
package info.shylie.ashes.api.rune;
import info.shylie.ashes.AshesRegistry;
public class RuneType<T extends Rune> {
private final RuneFactory<T> factory;
protected RuneType(RuneFactory<T> 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 extends Rune> {
T create(RuneType<T> type);
}
public static class Builder<T extends Rune> {
private final RuneFactory<T> factory;
private Builder(RuneFactory<T> factory) {
this.factory = factory;
}
public RuneType<T> build() {
return new RuneType<>(factory);
}
public static <T extends Rune> Builder<T> of(RuneFactory<T> factory) {
return new Builder<>(factory);
}
}
}

View File

@ -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<? extends TaskRune> 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);
}

View File

@ -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);
}
}
}

View File

@ -17,7 +17,7 @@ public class ItemModels extends ItemModelProvider {
@Override @Override
protected void registerModels() { protected void registerModels() {
basicItem(AshesRegistry.HEATED_ASHY_INGOT.get()); basicItem(AshesRegistry.HEATED_ASHY_INGOT.get());
itemWithTexture(AshesRegistry.PARAM_RUNE, "rune"); basicItem(AshesRegistry.RUNE.get());
spawnEgg(AshesRegistry.ASHEN_GOLEM_SPAWN_EGG); spawnEgg(AshesRegistry.ASHEN_GOLEM_SPAWN_EGG);
} }

View File

@ -1,7 +1,12 @@
package info.shylie.ashes.entity; package info.shylie.ashes.entity;
import com.rekindled.embers.datagen.EmbersSounds; 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.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType; 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 net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class AshenGolemEntity extends AbstractGolem { public class AshenGolemEntity extends AbstractGolem implements IGolem {
public AshenGolemEntity(EntityType<? extends AbstractGolem> entityType, Level level) { private TaskRune task;
public AshenGolemEntity(EntityType<? extends AshenGolemEntity> entityType, Level level) {
super(entityType, level); super(entityType, level);
} }
@ -37,4 +44,34 @@ public class AshenGolemEntity extends AbstractGolem {
super.playStepSound(pos, state); super.playStepSound(pos, state);
playSound(EmbersSounds.ANCIENT_GOLEM_STEP.get()); 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));
}
} }

View File

@ -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();
}
}

View File

@ -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<ItemStack> 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;
}
}

View File

@ -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<ItemStack> 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<TooltipComponent> 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<Component> 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);
}
}

View File

@ -1,34 +1,47 @@
package info.shylie.ashes.rune.param; 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.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.Component;
import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.context.UseOnContext;
public class BlockPosRune implements IParamRune<BlockPos> { public class BlockPosRune extends ParameterRune<BlockPos> {
@Override private static final String X_TAG = "x";
public String getDescription(ItemStack stack) { private static final String Y_TAG = "y";
BlockPos pos = getParameter(stack); private static final String Z_TAG = "z";
if (pos == null) { return null; }
return String.format("(%d, %d, %d)", pos.getX(), pos.getY(), pos.getZ()); public BlockPosRune(RuneType<? extends BlockPosRune> type) {
super(type);
} }
@Override @Override
public BlockPos getParameter(ItemStack stack) { public Component getTooltip() {
CompoundTag tag = stack.getOrCreateTag(); BlockPos pos = getValue();
if (!tag.contains("x") || !tag.contains("y") || !tag.contains("z")) { if (pos == null) {
return 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 @Override
public boolean setParameter(UseOnContext context) { protected void saveImpl(CompoundTag tag) {
CompoundTag tag = context.getItemInHand().getOrCreateTag(); tag.putInt(X_TAG, getValue().getX());
tag.putInt("x", context.getClickedPos().getX()); tag.putInt(Y_TAG, getValue().getY());
tag.putInt("y", context.getClickedPos().getY()); tag.putInt(Z_TAG, getValue().getZ());
tag.putInt("z", context.getClickedPos().getZ()); }
return true;
@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();
} }
} }

View File

@ -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<Item> {
private static final String ITEM_TAG = "item";
public ItemRune(RuneType<? extends ItemRune> 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());
}
}

View File

@ -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<? extends MoveRune> 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
);
}
}

View File

@ -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<? extends SelectorRune> 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;
}
}

View File

@ -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<? extends SequenceRune> 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;
}
}

View File

@ -0,0 +1,7 @@
{
"rune.ashes.blockpos.default": "No position set",
"item.ashes.rune": "Rune",
"itemgroup.ashes": "Ashes"
}