diff --git a/src/main/java/dev/zontreck/essentials/AriasEssentials.java b/src/main/java/dev/zontreck/essentials/AriasEssentials.java index 93825ec..16166a6 100644 --- a/src/main/java/dev/zontreck/essentials/AriasEssentials.java +++ b/src/main/java/dev/zontreck/essentials/AriasEssentials.java @@ -8,11 +8,15 @@ import java.util.UUID; import dev.zontreck.ariaslib.util.DelayedExecutorService; import dev.zontreck.essentials.client.Keybindings; +import dev.zontreck.essentials.client.renderer.TimeBoostEntityRenderer; import dev.zontreck.essentials.commands.teleport.TeleportActioner; import dev.zontreck.essentials.configs.client.AEClientConfig; import dev.zontreck.essentials.configs.server.AEServerConfig; +import dev.zontreck.essentials.entities.ModEntities; import dev.zontreck.essentials.events.TeleportEvent; -import dev.zontreck.essentials.gui.HeartsRenderer; +import dev.zontreck.essentials.client.renderer.HeartsRenderer; +import dev.zontreck.essentials.items.CreativeModeTabs; +import dev.zontreck.essentials.items.ModItems; import dev.zontreck.essentials.networking.ModMessages; import dev.zontreck.essentials.rtp.RTPCaches; import dev.zontreck.essentials.rtp.RTPCachesEventHandlers; @@ -21,6 +25,7 @@ import dev.zontreck.essentials.util.CommandCooldowns; import dev.zontreck.libzontreck.util.ChatHelpers; import dev.zontreck.libzontreck.util.ServerUtilities; import dev.zontreck.libzontreck.vectors.WorldPosition; +import net.minecraft.client.renderer.entity.EntityRenderers; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraftforge.api.distmarker.Dist; @@ -76,6 +81,10 @@ public class AriasEssentials { MinecraftForge.EVENT_BUS.register(new RTPCachesEventHandlers()); MinecraftForge.EVENT_BUS.register(new CommandCooldowns()); MinecraftForge.EVENT_BUS.register(RTPCachesEventHandlers.class); + + ModItems.register(bus); + ModEntities.register(bus); + CreativeModeTabs.register(bus); } @SubscribeEvent @@ -142,6 +151,7 @@ public class AriasEssentials { LOGGER.info("Client setup"); + EntityRenderers.register(ModEntities.TIAB_ENTITY.get(), TimeBoostEntityRenderer::new); MinecraftForge.EVENT_BUS.register(new HeartsRenderer()); } diff --git a/src/main/java/dev/zontreck/essentials/gui/HeartsRenderer.java b/src/main/java/dev/zontreck/essentials/client/renderer/HeartsRenderer.java similarity index 99% rename from src/main/java/dev/zontreck/essentials/gui/HeartsRenderer.java rename to src/main/java/dev/zontreck/essentials/client/renderer/HeartsRenderer.java index d8df687..5eb2b37 100644 --- a/src/main/java/dev/zontreck/essentials/gui/HeartsRenderer.java +++ b/src/main/java/dev/zontreck/essentials/client/renderer/HeartsRenderer.java @@ -7,7 +7,7 @@ * */ -package dev.zontreck.essentials.gui; +package dev.zontreck.essentials.client.renderer; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; diff --git a/src/main/java/dev/zontreck/essentials/client/renderer/TimeBoostEntityRenderer.java b/src/main/java/dev/zontreck/essentials/client/renderer/TimeBoostEntityRenderer.java new file mode 100644 index 0000000..1ed3bf2 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/client/renderer/TimeBoostEntityRenderer.java @@ -0,0 +1,60 @@ +package dev.zontreck.essentials.client.renderer; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import dev.zontreck.essentials.entities.TimeBoostEntity; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +public class TimeBoostEntityRenderer extends EntityRenderer +{ + + public TimeBoostEntityRenderer(EntityRendererProvider.Context erp) { + super(erp); + } + + + + @Override + public void render(TimeBoostEntity entity, float entityYaw, float partialTicks, PoseStack matrixStack, MultiBufferSource bufferIn, int packedLightIn) { + String timeRate = "x" + 2 * entity.getTimeRate(); + float paddingLeftRight = 2 * entity.getTimeRate() < 10 ? 0.11F : 0.19F; + drawText(matrixStack, bufferIn, timeRate, new Vector3f(-paddingLeftRight, 0.064F, 0.51F), Axis.YP.rotationDegrees(0), ChatFormatting.WHITE.getColor()); // Front + drawText(matrixStack, bufferIn, timeRate, new Vector3f(paddingLeftRight, 0.064F, -0.51F), Axis.YP.rotationDegrees(180F), ChatFormatting.WHITE.getColor()); // Back + drawText(matrixStack, bufferIn, timeRate, new Vector3f(0.51F, 0.064F, paddingLeftRight), Axis.YP.rotationDegrees(90F), ChatFormatting.WHITE.getColor()); // Right + drawText(matrixStack, bufferIn, timeRate, new Vector3f(-0.51F, 0.064F, -paddingLeftRight), Axis.YP.rotationDegrees(-90F), ChatFormatting.WHITE.getColor()); // Left + drawText(matrixStack, bufferIn, timeRate, new Vector3f(-paddingLeftRight, 0.51F, -0.064F), Axis.XP.rotationDegrees(90F), ChatFormatting.WHITE.getColor()); // Top + drawText(matrixStack, bufferIn, timeRate, new Vector3f(-paddingLeftRight, -0.51F, 0.064F), Axis.XP.rotationDegrees(-90F), ChatFormatting.WHITE.getColor()); // Bottom + } + + @Override + public ResourceLocation getTextureLocation(TimeBoostEntity entity) { + return null; + } + + private void drawText(PoseStack matrixStack, MultiBufferSource source, String text, Vector3f translateVector, Quaternionf rotate, int color) { + matrixStack.pushPose(); + matrixStack.translate(translateVector.x(), translateVector.y(), translateVector.z()); + matrixStack.scale(0.02F, -0.02F, 0.02F); + matrixStack.mulPose(rotate); + getFont().drawInBatch( + text, + 0, + 0, + -1, + false, + matrixStack.last().pose(), + source, + Font.DisplayMode.NORMAL, + 0, + color + ); + matrixStack.popPose(); + } +} diff --git a/src/main/java/dev/zontreck/essentials/configs/NBTKeys.java b/src/main/java/dev/zontreck/essentials/configs/NBTKeys.java new file mode 100644 index 0000000..6bfc121 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/configs/NBTKeys.java @@ -0,0 +1,13 @@ +package dev.zontreck.essentials.configs; + + +public class NBTKeys { + // for time in a bottle item + public static final String STORED_TIME = "storedTime"; + public static final String TOTAL_ACCUMULATED_TIME = "totalAccumulatedTime"; + + // for time accelerator entity + public static final String ENTITY_TIME_RATE = "timeRate"; + public static final String ENTITY_REMAINING_TIME = "remainingTime"; + public static final String ENTITY_POS = "position"; +} diff --git a/src/main/java/dev/zontreck/essentials/configs/server/AEServerConfig.java b/src/main/java/dev/zontreck/essentials/configs/server/AEServerConfig.java index 35d0879..686bbb9 100644 --- a/src/main/java/dev/zontreck/essentials/configs/server/AEServerConfig.java +++ b/src/main/java/dev/zontreck/essentials/configs/server/AEServerConfig.java @@ -4,6 +4,7 @@ import dev.zontreck.ariaslib.util.Lists; import dev.zontreck.essentials.configs.server.sections.*; import dev.zontreck.essentials.util.EssentialsDatastore; import dev.zontreck.essentials.util.Maps; +import dev.zontreck.libzontreck.chat.ChatColor; import dev.zontreck.libzontreck.util.SNbtIo; import net.minecraft.nbt.*; @@ -20,6 +21,7 @@ public class AEServerConfig public Back back; public Teleportation teleport; public Messages messages; + public Bottles bottles; @@ -38,6 +40,10 @@ public class AEServerConfig config.back = Back.deserialize(tag.getCompound(Back.TAG_NAME)); config.teleport = Teleportation.deserialize(tag.getCompound(Teleportation.TAG_NAME)); config.messages = Messages.deserialize(tag.getCompound(Messages.TAG_NAME)); + if(tag.contains(Bottles.TAG_NAME)) + { + config.bottles = Bottles.deserialize(tag.getCompound(Bottles.TAG_NAME)); + } else config.bottles = new Bottles(); return config; @@ -88,6 +94,7 @@ public class AEServerConfig "witherstormmod:bowels" ); messages = new Messages(); + bottles = new Bottles(); @@ -116,6 +123,7 @@ public class AEServerConfig tag.put(Back.TAG_NAME, back.serialize()); tag.put(Teleportation.TAG_NAME, teleport.serialize()); tag.put(Messages.TAG_NAME, messages.serialize()); + tag.put(Bottles.TAG_NAME, bottles.serialize()); diff --git a/src/main/java/dev/zontreck/essentials/configs/server/sections/Bottles.java b/src/main/java/dev/zontreck/essentials/configs/server/sections/Bottles.java new file mode 100644 index 0000000..9d96d17 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/configs/server/sections/Bottles.java @@ -0,0 +1,76 @@ +package dev.zontreck.essentials.configs.server.sections; + +import net.minecraft.nbt.CompoundTag; + +public class Bottles { + public static final String TAG_NAME = "bottles"; + public static final String TAG_DURATION = "durationEachUse"; + public static final String TAG_TICKS = "ticks"; + public static final String TAG_RANDOM_TICKS = "avgRandomTicks"; + public static final String TAG_MAX_TIME_RATE = "maxTimeRate"; + public static final String TAG_STORED_TIME = "maxTime"; + + public static final String TAG_MESSAGE_STORED_TIME = "msg_storedTime"; + public static final String TAG_MESSAGE_ACCUMULATED_TIME = "msg_accumulatedTime"; + + + + + public int eachUseDuration = 30; + public int ticks = 20; + public int avgRandomTicks = 512; + public int maxTimeRate = 8; + public int maxTime = (60 * 60 * 24 * 30); // 30 days is the default + public String storedTimeStr = "!Dark_Green!Stored Time: [0]:[1]:[2]"; + public String accumulatedTimeStr = "!Gray!Total Accumulated Time: [0]:[1]:[2]"; + + + + public CompoundTag serialize() + { + CompoundTag tag = new CompoundTag(); + tag.putInt(TAG_DURATION, eachUseDuration); + tag.putInt(TAG_TICKS, ticks); + tag.putInt(TAG_RANDOM_TICKS, avgRandomTicks); + tag.putInt(TAG_MAX_TIME_RATE, maxTimeRate); + tag.putString(TAG_MESSAGE_STORED_TIME, storedTimeStr); + tag.putString(TAG_MESSAGE_ACCUMULATED_TIME, accumulatedTimeStr); + tag.putInt(TAG_STORED_TIME, maxTime); + + + return tag; + } + + public static Bottles deserialize(CompoundTag tag) + { + Bottles bottles = new Bottles(); + if(tag.contains(TAG_DURATION)) + { + bottles.eachUseDuration = tag.getInt(TAG_DURATION); + } + + if(tag.contains(TAG_TICKS)) + bottles.ticks = tag.getInt(TAG_TICKS); + + if(tag.contains(TAG_RANDOM_TICKS)) + bottles.avgRandomTicks = tag.getInt(TAG_RANDOM_TICKS); + + if(tag.contains(TAG_MAX_TIME_RATE)) + bottles.maxTimeRate = tag.getInt(TAG_MAX_TIME_RATE); + + if(tag.contains(TAG_MESSAGE_STORED_TIME)) + bottles.storedTimeStr = tag.getString(TAG_MESSAGE_STORED_TIME); + + if(tag.contains(TAG_MESSAGE_ACCUMULATED_TIME)) + bottles.accumulatedTimeStr = tag.getString(TAG_MESSAGE_ACCUMULATED_TIME); + + if(tag.contains(TAG_STORED_TIME)) + bottles.maxTime = tag.getInt(TAG_STORED_TIME); + + + + + return bottles; + } + +} diff --git a/src/main/java/dev/zontreck/essentials/entities/ModEntities.java b/src/main/java/dev/zontreck/essentials/entities/ModEntities.java new file mode 100644 index 0000000..394500f --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/entities/ModEntities.java @@ -0,0 +1,27 @@ +package dev.zontreck.essentials.entities; + +import dev.zontreck.essentials.AriasEssentials; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +public class ModEntities +{ + public static final DeferredRegister> REGISTER = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, AriasEssentials.MODID); + + public static RegistryObject> TIAB_ENTITY = REGISTER.register("tiab_entity_type", ()->EntityType.Builder.of(TimeBoostEntity::new, MobCategory.MISC) + .sized(0.1f, 0.1f) + .build(new ResourceLocation(AriasEssentials.MODID, "tiab_entity_type").toString()) + ); + + + public static void register(IEventBus bus) { + REGISTER.register(bus); + } +} diff --git a/src/main/java/dev/zontreck/essentials/entities/TimeBoostEntity.java b/src/main/java/dev/zontreck/essentials/entities/TimeBoostEntity.java new file mode 100644 index 0000000..bd26f43 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/entities/TimeBoostEntity.java @@ -0,0 +1,128 @@ +package dev.zontreck.essentials.entities; + +import dev.zontreck.essentials.configs.NBTKeys; +import dev.zontreck.essentials.configs.server.AEServerConfig; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.network.NetworkHooks; + +public class TimeBoostEntity extends Entity +{ + private static final EntityDataAccessor timeRate = SynchedEntityData.defineId(TimeBoostEntity.class, EntityDataSerializers.INT); + + private int remainingTime; + private BlockPos position; + + + public TimeBoostEntity(EntityType entityType, Level worldIn) { + super(entityType, worldIn); + entityData.set(timeRate, 1); + } + + + public TimeBoostEntity(Level worldIn, BlockPos pos, double posX, double posY, double posZ) { + this(ModEntities.TIAB_ENTITY.get(), worldIn); + this.position = pos; + this.setPos(posX, posY, posZ); + } + + @Override + protected void defineSynchedData() { + entityData.define(timeRate, 1); + } + + + + @Override + protected void readAdditionalSaveData(CompoundTag compound) { + entityData.set(timeRate, compound.getInt(NBTKeys.ENTITY_TIME_RATE)); + setRemainingTime(compound.getInt(NBTKeys.ENTITY_REMAINING_TIME)); + this.position = NbtUtils.readBlockPos(compound.getCompound(NBTKeys.ENTITY_POS)); + } + + @Override + protected void addAdditionalSaveData(CompoundTag compound) { + compound.putInt(NBTKeys.ENTITY_TIME_RATE, getTimeRate()); + compound.putInt(NBTKeys.ENTITY_REMAINING_TIME, getRemainingTime()); + compound.put(NBTKeys.ENTITY_POS, NbtUtils.writeBlockPos(this.position)); + } + + @Override + public Packet getAddEntityPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } + + public int getTimeRate() { + return entityData.get(timeRate); + } + + public void setTimeRate(int rate) { + entityData.set(timeRate, rate); + } + + public int getRemainingTime() { + return this.remainingTime; + } + + public void setRemainingTime(int remainingTime) { + this.remainingTime = remainingTime; + } + + @Override + public void tick() { + super.tick(); + Level level = level(); + + if(position == null) + { + if(!level.isClientSide) + { + remove(RemovalReason.KILLED); + } + return; + } + + BlockState state = level.getBlockState(position); + ServerLevel serverWorld = level.getServer().getLevel(level.dimension()); + BlockEntity targetTE = level.getBlockEntity(position); + + + for (int i = 0; i < getTimeRate(); i++) { + if (targetTE != null) { + // if is TileEntity (furnace, brewing stand, ...) + BlockEntityTicker ticker = targetTE.getBlockState().getTicker(level, (BlockEntityType) targetTE.getType()); + if (ticker != null) { + ticker.tick(level, position, targetTE.getBlockState(), targetTE); + } + } else if (serverWorld != null && state.isRandomlyTicking()) { + // if is random ticket block (grass block, sugar cane, wheat or sapling, ...) + if (level.random.nextInt(AEServerConfig.getInstance().bottles.avgRandomTicks) == 0) { + state.randomTick(serverWorld, position, level.random); + } + } else { + // block entity broken + this.remove(RemovalReason.KILLED); + break; + } + } + + this.remainingTime -= 1; + if (this.remainingTime <= 0 && !level.isClientSide) { + this.remove(RemovalReason.KILLED); + } + } +} diff --git a/src/main/java/dev/zontreck/essentials/items/CreativeModeTabs.java b/src/main/java/dev/zontreck/essentials/items/CreativeModeTabs.java new file mode 100644 index 0000000..c276910 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/items/CreativeModeTabs.java @@ -0,0 +1,39 @@ +package dev.zontreck.essentials.items; + +import dev.zontreck.essentials.AriasEssentials; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.ItemLike; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +@Mod.EventBusSubscriber(modid = AriasEssentials.MODID, value = Dist.CLIENT) +public class CreativeModeTabs { + public static DeferredRegister REGISTER = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, AriasEssentials.MODID); + + public static void register(IEventBus bus) { + REGISTER.register(bus); + } + public static final List> AE_TAB_ITEMS = new ArrayList<>(); + public static RegistryObject AE_GAME_TAB = REGISTER.register("ariasessentials", ()->CreativeModeTab.builder() + .icon(ModItems.TIME_IN_A_BOTTLE.get()::getDefaultInstance) + .title(Component.translatable("itemGroup.tabs.ariasessentials")) + .displayItems((display, output) -> AE_TAB_ITEMS.forEach(it->output.accept(it.get()))) + .build()); + + public static RegistryObject addToAETab(RegistryObject item) + { + AE_TAB_ITEMS.add(item); + return item; + } +} diff --git a/src/main/java/dev/zontreck/essentials/items/EventHandlers.java b/src/main/java/dev/zontreck/essentials/items/EventHandlers.java new file mode 100644 index 0000000..caa1f21 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/items/EventHandlers.java @@ -0,0 +1,11 @@ +package dev.zontreck.essentials.items; + +import dev.zontreck.essentials.AriasEssentials; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = AriasEssentials.MODID) +public class EventHandlers +{ +} diff --git a/src/main/java/dev/zontreck/essentials/items/ModItems.java b/src/main/java/dev/zontreck/essentials/items/ModItems.java new file mode 100644 index 0000000..f696837 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/items/ModItems.java @@ -0,0 +1,20 @@ +package dev.zontreck.essentials.items; + +import dev.zontreck.essentials.AriasEssentials; +import dev.zontreck.essentials.items.implementation.TimeBottle; +import net.minecraft.world.item.Item; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +public class ModItems +{ + public static DeferredRegister REGISTRY = DeferredRegister.create(ForgeRegistries.ITEMS, AriasEssentials.MODID); + + public static void register(IEventBus bus) { + REGISTRY.register(bus); + } + + public static RegistryObject TIME_IN_A_BOTTLE = CreativeModeTabs.addToAETab(REGISTRY.register("tiab", ()->new TimeBottle())); +} diff --git a/src/main/java/dev/zontreck/essentials/items/abstraction/AbstractBottle.java b/src/main/java/dev/zontreck/essentials/items/abstraction/AbstractBottle.java new file mode 100644 index 0000000..5518522 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/items/abstraction/AbstractBottle.java @@ -0,0 +1,148 @@ +package dev.zontreck.essentials.items.abstraction; + +import dev.zontreck.essentials.configs.server.AEServerConfig; +import dev.zontreck.essentials.entities.TimeBoostEntity; +import dev.zontreck.essentials.util.SoundUtilities; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +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; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; + +import java.util.Optional; + +public abstract class AbstractBottle extends Item { + private static final String[] NOTES = {"C", "D", "E", "F", "G2", "A2", "B2", "C2", "D2", "E2", "F2"}; + + public AbstractBottle() { + super(new Properties().stacksTo(1)); + } + + @Override + public InteractionResult useOn(UseOnContext context) { + Level level = context.getLevel(); + + if (level.isClientSide) { + return InteractionResult.PASS; + } + + BlockPos pos = context.getClickedPos(); + BlockState blockState = level.getBlockState(pos); + BlockEntity targetTE = level.getBlockEntity(pos); + ItemStack stack = context.getItemInHand(); + Player player = context.getPlayer(); + + if (targetTE == null && !blockState.isRandomlyTicking()) { + return InteractionResult.FAIL; + } + + int nextRate = 1; + int energyRequired = getEnergyCost(nextRate); + boolean isCreativeMode = player != null && player.isCreative(); + + Optional o = level.getEntitiesOfClass(TimeBoostEntity.class, new AABB(pos)).stream().findFirst(); + + if (o.isPresent()) { + TimeBoostEntity entityTA = o.get(); + int currentRate = entityTA.getTimeRate(); + int usedUpTime = getEachUseDuration() - entityTA.getRemainingTime(); + + if (currentRate >= Math.pow(2, AEServerConfig.getInstance().bottles.maxTimeRate - 1)) { + return InteractionResult.SUCCESS; + } + + nextRate = currentRate * 2; + int timeAdded = usedUpTime / 2; + energyRequired = getEnergyCost(nextRate); + + if (!canUse(stack, isCreativeMode, energyRequired)) { + return InteractionResult.SUCCESS; + } + + entityTA.setTimeRate(nextRate); + entityTA.setRemainingTime(entityTA.getRemainingTime() + timeAdded); + } else { + // First use + if (!canUse(stack, isCreativeMode, energyRequired)) { + return InteractionResult.SUCCESS; + } + + TimeBoostEntity entityTA = new TimeBoostEntity(level, pos, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + entityTA.setRemainingTime(getEachUseDuration()); + level.addFreshEntity(entityTA); + } + + if (!isCreativeMode) { + this.applyDamage(stack, energyRequired); + } + playSound(level, pos, nextRate); + + return InteractionResult.SUCCESS; + } + + protected int getEachUseDuration() { + return AEServerConfig.getInstance().bottles.ticks * AEServerConfig.getInstance().bottles.eachUseDuration; + } + + public int getEnergyCost(int timeRate) { + if (timeRate <= 1) return getEachUseDuration(); + return timeRate / 2 * getEachUseDuration(); + } + + public boolean canUse(ItemStack stack, boolean isCreativeMode, int energyRequired) { + return getStoredEnergy(stack) >= energyRequired || isCreativeMode; + } + + protected abstract int getStoredEnergy(ItemStack stack); + + protected abstract void setStoredEnergy(ItemStack stack, int energy); + + protected abstract void applyDamage(ItemStack stack, int damage); + + public void playSound(Level level, BlockPos pos, int nextRate) { + switch (nextRate) { + case 1: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[0]); + break; + case 2: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[1]); + break; + case 4: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[2]); + break; + case 8: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[3]); + break; + case 16: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[4]); + break; + case 32: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[5]); + break; + case 64: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[6]); + break; + case 128: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[7]); + break; + case 256: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[8]); + break; + case 512: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[9]); + break; + default: + SoundUtilities.playNoteBlockHarpSound(level, pos, NOTES[10]); + } + } + + @Override + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) { + return !ItemStack.isSameItem(oldStack, newStack); + } +} \ No newline at end of file diff --git a/src/main/java/dev/zontreck/essentials/items/implementation/TimeBottle.java b/src/main/java/dev/zontreck/essentials/items/implementation/TimeBottle.java new file mode 100644 index 0000000..7cadd35 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/items/implementation/TimeBottle.java @@ -0,0 +1,106 @@ +package dev.zontreck.essentials.items.implementation; + +import dev.zontreck.essentials.configs.NBTKeys; +import dev.zontreck.essentials.configs.server.AEServerConfig; +import dev.zontreck.essentials.items.abstraction.AbstractBottle; +import dev.zontreck.essentials.util.StylesUtil; +import dev.zontreck.libzontreck.util.ChatHelpers; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; + +import java.util.List; + +public class TimeBottle extends AbstractBottle +{ + + public TimeBottle() { + super(); + } + @Override + public boolean isFoil(ItemStack stack) { + sanityCheck(stack); + + return stack.getTag().contains(NBTKeys.STORED_TIME); + } + + private void sanityCheck(ItemStack stack) { + + if(!stack.hasTag() || stack.getTag()==null) + { + stack.setTag(new CompoundTag()); + } + } + + @Override + public void appendHoverText(ItemStack itemStack, Level world, List tooltip, TooltipFlag flag) { + super.appendHoverText(itemStack, world, tooltip, flag); + + int storedTime = this.getStoredEnergy(itemStack); + int storedSeconds = storedTime / AEServerConfig.getInstance().bottles.ticks; + int hours = storedSeconds / 3600; + int minutes = (storedSeconds % 3600) / 60; + int seconds = storedSeconds % 60; + + int totalAccumulatedTime = this.getTotalAccumulatedTime(itemStack); + int totalAccumulatedTimeSeconds = totalAccumulatedTime / AEServerConfig.getInstance().bottles.ticks; + int totalAccumulatedHours = totalAccumulatedTimeSeconds / 3600; + int totalAccumulatedMinutes = (totalAccumulatedTimeSeconds % 3600) / 60; + int totalAccumulatedSeconds = totalAccumulatedTimeSeconds % 60; + + tooltip.add(ChatHelpers.macro(AEServerConfig.getInstance().bottles.storedTimeStr, "" + hours, "" + minutes, "" + seconds)); + + tooltip.add(ChatHelpers.macro(AEServerConfig.getInstance().bottles.accumulatedTimeStr, "" + totalAccumulatedHours, "" + totalAccumulatedMinutes, "" + totalAccumulatedSeconds)); + } + + @Override + public int getStoredEnergy(ItemStack stack) { + return stack.getOrCreateTag().getInt(NBTKeys.STORED_TIME); + } + + @Override + public void setStoredEnergy(ItemStack stack, int energy) { + int newStoredTime = Math.min(energy, AEServerConfig.getInstance().bottles.maxTime); + stack.getOrCreateTag().putInt(NBTKeys.STORED_TIME, newStoredTime); + } + + @Override + public void applyDamage(ItemStack stack, int damage) { + setStoredEnergy(stack, getStoredEnergy(stack) - damage); + } + + public int getTotalAccumulatedTime(ItemStack stack) { + return stack.getOrCreateTag().getInt(NBTKeys.TOTAL_ACCUMULATED_TIME); + } + + public void setTotalAccumulatedTime(ItemStack stack, int value) { + int newValue = Math.min(value, AEServerConfig.getInstance().bottles.maxTime); + stack.getOrCreateTag().putInt(NBTKeys.TOTAL_ACCUMULATED_TIME, newValue); + } + + + @Override + public void inventoryTick(ItemStack itemStack, Level level, Entity entity, int itemSlot, boolean isSelected) { + super.inventoryTick(itemStack, level, entity, itemSlot, isSelected); + if (level.isClientSide) { + return; + } + + if (level.getGameTime() % AEServerConfig.getInstance().bottles.ticks == 0) { + int storedTime = this.getStoredEnergy(itemStack); + if (storedTime < AEServerConfig.getInstance().bottles.maxTime) { + this.setStoredEnergy(itemStack, storedTime + AEServerConfig.getInstance().bottles.ticks); + } + + int totalAccumulatedTime = this.getTotalAccumulatedTime(itemStack); + if (totalAccumulatedTime < AEServerConfig.getInstance().bottles.maxTime) { + this.setTotalAccumulatedTime(itemStack, totalAccumulatedTime + AEServerConfig.getInstance().bottles.ticks); + } + } + } +} diff --git a/src/main/java/dev/zontreck/essentials/util/SoundUtilities.java b/src/main/java/dev/zontreck/essentials/util/SoundUtilities.java new file mode 100644 index 0000000..7a6ab82 --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/util/SoundUtilities.java @@ -0,0 +1,47 @@ +package dev.zontreck.essentials.util; + + +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.Level; + +/** + * {@link SoundUtilities#playNoteBlockHarpSound(Level, BlockPos, String)} was taken/derrived from Time-in-a-bottle github. + */ +public class SoundUtilities { + public static void playNoteBlockHarpSound(Level level, BlockPos pos, String note) { + // https://minecraft.gamepedia.com/Note_Block + switch (note) { + // Octave 1 + case "F#" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.5F); + case "G" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.529732F); + case "G#" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.561231F); + case "A" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.594604F); + case "A#" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.629961F); + case "B" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.667420F); + case "C" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.707107F); + case "C#" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.749154F); + case "D" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.793701F); + case "D#" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.840896F); + case "E" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.890899F); + case "F" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 0.943874F); + + + // Octave 2 + case "F#2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1F); + case "G2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.059463F); + case "G#2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.122462F); + case "A2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.189207F); + case "A#2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.259921F); + case "B2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.334840F); + case "C2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.414214F); + case "C#2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.498307F); + case "D2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.587401F); + case "D#2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.681793F); + case "E2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.781797F); + case "F2" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 1.887749F); + case "F#3" -> level.playSound(null, pos, SoundEvents.NOTE_BLOCK_HARP.get(), SoundSource.BLOCKS, 0.5F, 2F); + } + } +} diff --git a/src/main/java/dev/zontreck/essentials/util/StylesUtil.java b/src/main/java/dev/zontreck/essentials/util/StylesUtil.java new file mode 100644 index 0000000..df7c51b --- /dev/null +++ b/src/main/java/dev/zontreck/essentials/util/StylesUtil.java @@ -0,0 +1,10 @@ +package dev.zontreck.essentials.util; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Style; + +public class StylesUtil { + + public static final Style GREEN = Style.EMPTY.applyFormat(ChatFormatting.GREEN); + public static final Style GRAY = Style.EMPTY.applyFormat(ChatFormatting.GRAY); +} diff --git a/src/main/resources/assets/ariasessentials/lang/en_us.json b/src/main/resources/assets/ariasessentials/lang/en_us.json index 03ee39f..557f20d 100644 --- a/src/main/resources/assets/ariasessentials/lang/en_us.json +++ b/src/main/resources/assets/ariasessentials/lang/en_us.json @@ -1,4 +1,9 @@ { + "itemGroup.tabs.ariasessentials": "Aria's Essentials", + + "item.ariasessentials.tiab": "Time in a Bottle", + "item.ariasessentials.eiab": "Energy in a Bottle", + "key.category.ariasessentials": "Aria's Essentials", "key.ariasessentials.autowalk": "Auto Walk" } \ No newline at end of file diff --git a/src/main/resources/assets/ariasessentials/models/item/eiab.json b/src/main/resources/assets/ariasessentials/models/item/eiab.json new file mode 100644 index 0000000..47d2f64 --- /dev/null +++ b/src/main/resources/assets/ariasessentials/models/item/eiab.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ariasessentials:item/eiab" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ariasessentials/models/item/tiab.json b/src/main/resources/assets/ariasessentials/models/item/tiab.json new file mode 100644 index 0000000..4103a7a --- /dev/null +++ b/src/main/resources/assets/ariasessentials/models/item/tiab.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ariasessentials:item/tiab" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ariasessentials/textures/item/eiab.png b/src/main/resources/assets/ariasessentials/textures/item/eiab.png new file mode 100644 index 0000000..128cf9c Binary files /dev/null and b/src/main/resources/assets/ariasessentials/textures/item/eiab.png differ diff --git a/src/main/resources/assets/ariasessentials/textures/item/eiab.png.mcmeta b/src/main/resources/assets/ariasessentials/textures/item/eiab.png.mcmeta new file mode 100644 index 0000000..5cc283a --- /dev/null +++ b/src/main/resources/assets/ariasessentials/textures/item/eiab.png.mcmeta @@ -0,0 +1,6 @@ +{ + "animation": { + "frametime": 2, + "interpolate": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ariasessentials/textures/item/tiab.aseprite b/src/main/resources/assets/ariasessentials/textures/item/tiab.aseprite new file mode 100644 index 0000000..01d1501 Binary files /dev/null and b/src/main/resources/assets/ariasessentials/textures/item/tiab.aseprite differ diff --git a/src/main/resources/assets/ariasessentials/textures/item/tiab.png b/src/main/resources/assets/ariasessentials/textures/item/tiab.png new file mode 100644 index 0000000..6ee8853 Binary files /dev/null and b/src/main/resources/assets/ariasessentials/textures/item/tiab.png differ diff --git a/src/main/resources/assets/ariasessentials/textures/item/tiab.png.mcmeta b/src/main/resources/assets/ariasessentials/textures/item/tiab.png.mcmeta new file mode 100644 index 0000000..5cc283a --- /dev/null +++ b/src/main/resources/assets/ariasessentials/textures/item/tiab.png.mcmeta @@ -0,0 +1,6 @@ +{ + "animation": { + "frametime": 2, + "interpolate": true + } +} \ No newline at end of file