From 000b84e87eaedb94fb87ccb11e376c51f99a1d07 Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Wed, 10 Jan 2024 17:41:07 +0100 Subject: Added Energy Condenser --- src/main/java/lv/enes/mc/eris_alchemy/EMC.java | 36 +++- .../java/lv/enes/mc/eris_alchemy/EmcStorage.java | 17 ++ .../enes/mc/eris_alchemy/ErisAlchemyRegistry.java | 48 ++++- .../eris_alchemy/block/AlchemicalChestBlock.java | 10 +- .../enes/mc/eris_alchemy/block/ChestLikeBlock.java | 6 +- .../eris_alchemy/block/EnergyCondenserBlock.java | 56 ++++++ .../block/entity/AlchemicalChestBlockEntity.java | 46 ----- .../block/entity/AlchemicalChestEntity.java | 39 ++++ .../block/entity/ChestLikeBlockEntity.java | 200 -------------------- .../eris_alchemy/block/entity/ChestLikeEntity.java | 207 +++++++++++++++++++++ .../block/entity/EnergyCondenserEntity.java | 143 ++++++++++++++ .../mc/eris_alchemy/client/ChestLikeScreen.java | 4 +- .../eris_alchemy/client/EnergyCondenserScreen.java | 46 +++++ .../mc/eris_alchemy/client/ErisAlchemyClient.java | 10 +- .../client/ErisAlchemyClientRegistry.java | 11 +- .../eris_alchemy/client/ErisAlchemyMaterials.java | 5 + .../mc/eris_alchemy/menu/AlchemicalChestMenu.java | 20 +- .../mc/eris_alchemy/menu/ChestLikeBlockMenu.java | 67 ------- .../enes/mc/eris_alchemy/menu/ChestLikeMenu.java | 101 ++++++++++ .../mc/eris_alchemy/menu/EnergyCondenserMenu.java | 60 ++++++ .../mixin/ContainerOpenersCounterMixin.java | 56 ++++++ .../mc/eris_alchemy/mixin/client/SheetsMixin.java | 7 +- .../utils/ContainerOpenersCounterUtil.java | 20 ++ .../lv/enes/mc/eris_alchemy/utils/SyncedValue.java | 115 ++++++++++++ .../eris_alchemy/blockstates/energy_condenser.json | 7 + .../resources/assets/eris_alchemy/lang/en_us.json | 2 + .../models/block/energy_condenser.json | 5 + .../eris_alchemy/models/item/energy_condenser.json | 6 + .../en_us/entries/root/covalence_dusts.json | 1 - .../en_us/entries/root/energy_condenser.json | 14 ++ .../guide_book/en_us/entries/root/guide_book.json | 1 - .../textures/entity/chest/energy_condenser.png | Bin 0 -> 2649 bytes .../textures/gui/container/energy_condenser.png | Bin 0 -> 1331 bytes .../loot_tables/blocks/energy_condenser.json | 22 +++ .../eris_alchemy/recipes/energy_condenser.json | 22 +++ .../minecraft/tags/blocks/mineable/pickaxe.json | 3 +- src/main/resources/eris_alchemy.mixins.json | 1 + src/main/resources/quilt.mod.json | 7 +- 38 files changed, 1053 insertions(+), 368 deletions(-) create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/EmcStorage.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/EnergyCondenserBlock.java delete mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestEntity.java delete mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeEntity.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/block/entity/EnergyCondenserEntity.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/client/EnergyCondenserScreen.java delete mode 100644 src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeMenu.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/menu/EnergyCondenserMenu.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/mixin/ContainerOpenersCounterMixin.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/utils/ContainerOpenersCounterUtil.java create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/utils/SyncedValue.java create mode 100644 src/main/resources/assets/eris_alchemy/blockstates/energy_condenser.json create mode 100644 src/main/resources/assets/eris_alchemy/models/block/energy_condenser.json create mode 100644 src/main/resources/assets/eris_alchemy/models/item/energy_condenser.json create mode 100644 src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/energy_condenser.json create mode 100644 src/main/resources/assets/eris_alchemy/textures/entity/chest/energy_condenser.png create mode 100644 src/main/resources/assets/eris_alchemy/textures/gui/container/energy_condenser.png create mode 100644 src/main/resources/data/eris_alchemy/loot_tables/blocks/energy_condenser.json create mode 100644 src/main/resources/data/eris_alchemy/recipes/energy_condenser.json diff --git a/src/main/java/lv/enes/mc/eris_alchemy/EMC.java b/src/main/java/lv/enes/mc/eris_alchemy/EMC.java index e34d28f..3ff33ca 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/EMC.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/EMC.java @@ -16,6 +16,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import vazkii.patchouli.common.item.PatchouliItems; +import java.text.DecimalFormat; import java.util.*; import java.util.stream.Stream; @@ -62,6 +63,11 @@ public class EMC { private static final EMC defaultInstance = new EMC(null); private static final Map instances = Collections.synchronizedMap(new WeakHashMap<>()); + private static final DecimalFormat formatter = new DecimalFormat("0"); + static { + formatter.setMaximumFractionDigits(1); + } + public static EMC getInstance(Level world) { if (world == null) { return defaultInstance; @@ -76,6 +82,10 @@ public class EMC { return instance; } + public static String formatEmc(double value) { + return formatter.format(value); + } + private final Map data; private EMC(@Nullable Level world) { @@ -91,10 +101,9 @@ public class EMC { .forEach(holder -> data.putIfAbsent(holder.value().asItem(), emcValue)) ); - ErisAlchemy.LOGGER.info("Getting recipes..."); + ErisAlchemy.LOGGER.info("Calculating EMC values..."); var recipes = getRecipes(world); var configured = new HashSet<>(data.keySet()); - ErisAlchemy.LOGGER.info("Calculating EMC values..."); BuiltInRegistries.ITEM.forEach(item -> { configEmc(recipes, configured, item); if (world != null && !data.containsKey(item)) { @@ -103,11 +112,30 @@ public class EMC { }); } - public OptionalDouble get(Item item) { + public OptionalDouble get(ItemStack stack) { + if (stack.isEmpty()) { + return OptionalDouble.empty(); + } + + var item = stack.getItem(); var value = data.get(item); if (value == null || value <= 0) { return OptionalDouble.empty(); } + + EmcStorage storage = null; + if (item instanceof EmcStorage emcStorage) { + storage = emcStorage; + } else if (item instanceof BlockItem blockItem) { + if (blockItem.getBlock() instanceof EmcStorage emcStorage) { + storage = emcStorage; + } + } + + if (storage != null) { + value += storage.getStoredEmc(stack); + } + return OptionalDouble.of(value); } @@ -134,7 +162,7 @@ public class EMC { } private OptionalDouble configEmc(List recipes, Set configured, Item item) { - var res = get(item); + var res = get(item.getDefaultInstance()); if (res.isPresent() || configured.contains(item)) { return res; } diff --git a/src/main/java/lv/enes/mc/eris_alchemy/EmcStorage.java b/src/main/java/lv/enes/mc/eris_alchemy/EmcStorage.java new file mode 100644 index 0000000..427a887 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/EmcStorage.java @@ -0,0 +1,17 @@ +package lv.enes.mc.eris_alchemy; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; + +public interface EmcStorage { + double getStoredEmc(ItemStack stack, CompoundTag blockEntityData); + + default double getStoredEmc(ItemStack stack) { + var blockEntityData = BlockItem.getBlockEntityData(stack); + if (blockEntityData == null) { + return 0; + } + return getStoredEmc(stack, blockEntityData); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemyRegistry.java b/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemyRegistry.java index c02a1e6..592eb5e 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemyRegistry.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemyRegistry.java @@ -1,9 +1,13 @@ package lv.enes.mc.eris_alchemy; import lv.enes.mc.eris_alchemy.block.AlchemicalChestBlock; -import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity; +import lv.enes.mc.eris_alchemy.block.EnergyCondenserBlock; +import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestEntity; +import lv.enes.mc.eris_alchemy.block.entity.EnergyCondenserEntity; import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu; +import lv.enes.mc.eris_alchemy.menu.EnergyCondenserMenu; import lv.enes.mc.eris_alchemy.recipe.CovalenceRepair; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.flag.FeatureFlags; @@ -35,9 +39,15 @@ public final class ErisAlchemyRegistry { private static final Map> data = new LinkedHashMap<>(); - public static final BlockEntityType ALCHEMICAL_CHEST = register( + public static final BlockEntityType ALCHEMICAL_CHEST = register( "alchemical_chest", - QuiltBlockEntityTypeBuilder.create(AlchemicalChestBlockEntity::new, Blocks.ALCHEMICAL_CHEST) + QuiltBlockEntityTypeBuilder.create(AlchemicalChestEntity::new, Blocks.ALCHEMICAL_CHEST) + .build() + ); + + public static final BlockEntityType ENERGY_CONDENSER = register( + "energy_condenser", + QuiltBlockEntityTypeBuilder.create(EnergyCondenserEntity::new, Blocks.ENERGY_CONDENSER) .build() ); @@ -61,6 +71,11 @@ public final class ErisAlchemyRegistry { new AlchemicalChestBlock(QuiltBlockSettings.copy(ENDER_CHEST)) ); + public static final EnergyCondenserBlock ENERGY_CONDENSER = register( + "energy_condenser", + new EnergyCondenserBlock(QuiltBlockSettings.copy(ENDER_CHEST)) + ); + public static void consume(BiConsumer consumer) { data.forEach(consumer); } @@ -78,6 +93,9 @@ public final class ErisAlchemyRegistry { public static final Item ALCHEMICAL_CHEST = register(Blocks.ALCHEMICAL_CHEST, new QuiltItemSettings().rarity(Rarity.RARE)); + @SuppressWarnings("unused") + public static final Item ENERGY_CONDENSER = + register(Blocks.ENERGY_CONDENSER, new QuiltItemSettings().rarity(Rarity.EPIC)); public static final Item LOW_COVALENCE_DUST = register("low_covalence_dust", new Item(new QuiltItemSettings().rarity(Rarity.COMMON))); @SuppressWarnings("unused") @@ -112,6 +130,8 @@ public final class ErisAlchemyRegistry { public static final MenuType ALCHEMICAL_CHEST = register("alchemy_chest", AlchemicalChestMenu::new); + public static final ExtendedScreenHandlerType ENERGY_CONDENSER = + registerExt("energy_condenser", EnergyCondenserMenu::new); public static void consume(BiConsumer> consumer) { data.forEach(consumer); @@ -121,12 +141,32 @@ public final class ErisAlchemyRegistry { String id, MenuType.MenuSupplier supplier ) { - var menuType = new MenuType<>(supplier, FeatureFlags.VANILLA_SET); + return registerCommon(id, new MenuType<>(supplier, FeatureFlags.VANILLA_SET)); + } + + private static ExtendedScreenHandlerType registerExt( + String id, + ExtendedScreenHandlerType.ExtendedFactory supplier + ) { + return registerCommon(id, new ExtendedScreenHandlerType<>(supplier)); + } + + private static > T registerCommon( + String id, + T menuType + ) { data.put(new ResourceLocation(ErisAlchemy.ID, id), menuType); return menuType; } } + public static final class NetworkingConstants { + private NetworkingConstants() {} + + public static final ResourceLocation UPDATE_SYNCED_VALUE + = new ResourceLocation(ErisAlchemy.ID, "update_synced_value"); + } + public static final class RecipeSerializers { private RecipeSerializers() {} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java b/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java index d50ee77..eb7cb6e 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java @@ -3,15 +3,15 @@ package lv.enes.mc.eris_alchemy.block; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; -import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity; +import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestEntity; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -public class AlchemicalChestBlock extends ChestLikeBlock -{ - public static final Component CONTAINER_TITLE = Component.translatable("container.eris_alchemy.alchemical_chest"); +public class AlchemicalChestBlock extends ChestLikeBlock { + public static final Component CONTAINER_TITLE + = Component.translatable("container.eris_alchemy.alchemical_chest"); public AlchemicalChestBlock(Properties properties) { super(properties, () -> ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST); @@ -26,6 +26,6 @@ public class AlchemicalChestBlock extends ChestLikeBlock +public abstract class ChestLikeBlock extends AbstractChestBlock implements SimpleWaterloggedBlock { @@ -85,7 +85,7 @@ public abstract class ChestLikeBlock @Override public MenuProvider getMenuProvider(BlockState state, Level world, BlockPos pos) { - if (world.getBlockEntity(pos) instanceof ChestLikeBlockEntity entity) { + if (world.getBlockEntity(pos) instanceof ChestLikeEntity entity) { return new SimpleMenuProvider(entity, getContainerTitle()); } return null; diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/EnergyCondenserBlock.java b/src/main/java/lv/enes/mc/eris_alchemy/block/EnergyCondenserBlock.java new file mode 100644 index 0000000..86ac061 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/block/EnergyCondenserBlock.java @@ -0,0 +1,56 @@ +package lv.enes.mc.eris_alchemy.block; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import lv.enes.mc.eris_alchemy.EMC; +import lv.enes.mc.eris_alchemy.EmcStorage; +import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; +import lv.enes.mc.eris_alchemy.block.entity.EnergyCondenserEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.List; + +public class EnergyCondenserBlock extends ChestLikeBlock implements EmcStorage { + public static final Component CONTAINER_TITLE + = Component.translatable("container.eris_alchemy.energy_condenser"); + + public EnergyCondenserBlock(Properties properties) { + super(properties, () -> ErisAlchemyRegistry.BlockEntities.ENERGY_CONDENSER); + } + + @Override + public void appendHoverText( + ItemStack stack, + @Nullable BlockGetter world, + List tooltip, + TooltipFlag options + ) { + if (getStoredEmc(stack) >= 0.1) { + tooltip.add(Component.literal("Stored EMC: %s".formatted(EMC.formatEmc(getStoredEmc(stack))))); + } + } + + @Override + @Nonnull + protected Component getContainerTitle() { + return CONTAINER_TITLE; + } + + @Override + public double getStoredEmc(ItemStack stack, CompoundTag blockEntityData) { + return blockEntityData.getDouble("stored_emc"); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new EnergyCondenserEntity(pos, state); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java deleted file mode 100644 index bac49b6..0000000 --- a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java +++ /dev/null @@ -1,46 +0,0 @@ -package lv.enes.mc.eris_alchemy.block.entity; - -import jakarta.annotation.Nonnull; -import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; -import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu; -import lv.enes.mc.eris_alchemy.menu.ChestLikeBlockMenu; -import net.minecraft.core.BlockPos; -import net.minecraft.core.NonNullList; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; - -public class AlchemicalChestBlockEntity extends ChestLikeBlockEntity { - private final static int WIDTH = 13; - private final static int HEIGHT = 8; - - private final NonNullList items = NonNullList.withSize(WIDTH * HEIGHT, ItemStack.EMPTY); - - public AlchemicalChestBlockEntity(BlockPos pos, BlockState state) { - super(ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST, pos, state); - } - - @Nonnull - @Override - protected ChestLikeBlockMenu createMenu(int syncId, Inventory playerInventory) { - return new AlchemicalChestMenu(syncId, playerInventory, this); - } - - @Nonnull - @Override - protected Component getDefaultName() { - return Component.translatable("container.eris_alchemy.alchemical_chest"); - } - - @Override - protected NonNullList getItems() { - return items; - } - - @Override - protected Block getParent() { - return ErisAlchemyRegistry.Blocks.ALCHEMICAL_CHEST; - } -} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestEntity.java new file mode 100644 index 0000000..38ed902 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestEntity.java @@ -0,0 +1,39 @@ +package lv.enes.mc.eris_alchemy.block.entity; + +import jakarta.annotation.Nonnull; +import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; +import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu; +import lv.enes.mc.eris_alchemy.menu.ChestLikeMenu; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public class AlchemicalChestEntity extends ChestLikeEntity { + private final static int WIDTH = 13; + private final static int HEIGHT = 8; + + private final NonNullList items = NonNullList.withSize(WIDTH * HEIGHT, ItemStack.EMPTY); + + public AlchemicalChestEntity(BlockPos pos, BlockState state) { + super(ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST, pos, state); + } + + @Nonnull + @Override + protected ChestLikeMenu createMenu(int syncId, Inventory playerInventory) { + return new AlchemicalChestMenu(syncId, playerInventory, this); + } + + @Override + protected NonNullList getItems() { + return items; + } + + @Override + protected Block getParent() { + return ErisAlchemyRegistry.Blocks.ALCHEMICAL_CHEST; + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java deleted file mode 100644 index 48b3ad7..0000000 --- a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java +++ /dev/null @@ -1,200 +0,0 @@ -package lv.enes.mc.eris_alchemy.block.entity; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import lv.enes.mc.eris_alchemy.menu.ChestLikeBlockMenu; -import net.minecraft.core.BlockPos; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.*; -import net.minecraft.world.level.block.state.BlockState; - -public abstract class ChestLikeBlockEntity extends BaseContainerBlockEntity implements LidBlockEntity { - protected static final int EVENT_INTERACTED = 1; - - protected final ChestLidController lidController = new ChestLidController(); - protected final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { - @Override - protected void onOpen(Level world, BlockPos pos, BlockState state) { - ChestLikeBlockEntity.this.onOpen(world, pos); - } - - @Override - protected void onClose(Level world, BlockPos pos, BlockState state) { - ChestLikeBlockEntity.this.onClose(world, pos); - } - - @Override - protected void openerCountChanged(Level world, BlockPos pos, BlockState state, int oldViewerCount, int newViewerCount) { - world.blockEvent(worldPosition, getParent(), EVENT_INTERACTED, newViewerCount); - } - - @Override - protected boolean isOwnContainer(Player player) { - if (player.containerMenu instanceof ChestLikeBlockMenu menu) { - return menu.getContainer() == ChestLikeBlockEntity.this; - } - return false; - } - }; - - public ChestLikeBlockEntity( - BlockEntityType type, - BlockPos pos, - BlockState state - ) { - super(type, pos, state); - } - - protected abstract Block getParent(); - protected abstract NonNullList getItems(); - - @Override - public void clearContent() { - getItems().clear(); - } - - @Override - public int getContainerSize() { - return getItems().size(); - } - - @Nonnull - @Override - public ItemStack getItem(int slot) { - return getItems().get(slot); - } - - @Override - public float getOpenNess(float tickDelta) { - return lidController.getOpenness(tickDelta); - } - - @Override - public boolean isEmpty() { - return getItems().stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void load(CompoundTag nbt) { - super.load(nbt); - ContainerHelper.loadAllItems(nbt, getItems()); - } - - protected void onClose(@Nullable Level world, BlockPos pos) { - if (world == null) { - return; - } - - world.playSound( - null, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - SoundEvents.CHEST_OPEN, - SoundSource.BLOCKS, - 0.5f, - world.random.nextFloat() * 0.1f + 0.9f - ); - } - - protected void onOpen(@Nullable Level world, BlockPos pos) { - if (world == null) { - return; - } - - world.playSound( - null, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - SoundEvents.CHEST_CLOSE, - SoundSource.BLOCKS, - 0.5f, - world.random.nextFloat() * 0.1f + 0.9f - ); - } - - private void recheckOpen() { - if (!remove) { - openersCounter.recheckOpeners(getLevel(), getBlockPos(), getBlockState()); - } - } - - @Nonnull - @Override - public ItemStack removeItem(int slot, int amount) { - var stack = ContainerHelper.removeItem(getItems(), slot, amount); - if (!stack.isEmpty()) { - this.setChanged(); - } - return stack; - } - - @Nonnull - @Override - public ItemStack removeItemNoUpdate(int slot) { - return ContainerHelper.takeItem(getItems(), slot); - } - - @Override - protected void saveAdditional(CompoundTag nbt) { - super.saveAdditional(nbt); - ContainerHelper.saveAllItems(nbt, getItems()); - } - - @Override - public void setItem(int slot, ItemStack stack) { - getItems().set(slot, stack); - if (stack.getCount() > getMaxStackSize()) { - stack.setCount(getMaxStackSize()); - } - this.setChanged(); - } - - @Override - public void startOpen(Player player) { - if (!remove && !player.isSpectator()) { - openersCounter.incrementOpeners(player, getLevel(), getBlockPos(), getBlockState()); - } - } - - @Override - public boolean stillValid(Player player) { - return Container.stillValidBlockEntity(this, player); - } - - @Override - public void stopOpen(Player player) { - if (!remove && !player.isSpectator()) { - openersCounter.decrementOpeners(player, getLevel(), getBlockPos(), getBlockState()); - } - } - - public void tick(Level world, BlockPos ignoredPos, BlockState ignoredState) { - if (world.isClientSide) { - lidController.tickLid(); - } - recheckOpen(); - } - - - @Override - public boolean triggerEvent(int type, int data) { - if (type == EVENT_INTERACTED) { - lidController.shouldBeOpen(data > 0); - return true; - } - - return super.triggerEvent(type, data); - } -} - diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeEntity.java new file mode 100644 index 0000000..362b054 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeEntity.java @@ -0,0 +1,207 @@ +package lv.enes.mc.eris_alchemy.block.entity; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import lv.enes.mc.eris_alchemy.menu.ChestLikeMenu; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.*; +import net.minecraft.world.level.block.state.BlockState; + +public abstract class ChestLikeEntity extends BaseContainerBlockEntity implements LidBlockEntity { + protected static final int EVENT_INTERACTED = 1; + + protected final ChestLidController lidController = new ChestLidController(); + protected final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { + @Override + protected void onOpen(Level world, BlockPos pos, BlockState state) { + ChestLikeEntity.this.onOpen(world, pos); + } + + @Override + protected void onClose(Level world, BlockPos pos, BlockState state) { + ChestLikeEntity.this.onClose(world, pos); + } + + @Override + protected void openerCountChanged(Level world, BlockPos pos, BlockState state, int oldViewerCount, int newViewerCount) { + world.blockEvent(worldPosition, getParent(), EVENT_INTERACTED, newViewerCount); + } + + @Override + protected boolean isOwnContainer(Player player) { + if (player.containerMenu instanceof ChestLikeMenu menu) { + return menu.getContainer() == ChestLikeEntity.this; + } + return false; + } + }; + + public ChestLikeEntity( + BlockEntityType type, + BlockPos pos, + BlockState state + ) { + super(type, pos, state); + } + + protected abstract Block getParent(); + protected abstract NonNullList getItems(); + + @Override + public void clearContent() { + getItems().clear(); + } + + @Override + public int getContainerSize() { + return getItems().size(); + } + + @Nonnull + @Override + protected Component getDefaultName() { + return getBlockState().getBlock().getName(); + } + + @Nonnull + @Override + public ItemStack getItem(int slot) { + return getItems().get(slot); + } + + @Override + public float getOpenNess(float tickDelta) { + return lidController.getOpenness(tickDelta); + } + + @Override + public boolean isEmpty() { + return getItems().stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + ContainerHelper.loadAllItems(nbt, getItems()); + } + + protected void onClose(@Nullable Level world, BlockPos pos) { + if (world == null) { + return; + } + + world.playSound( + null, + pos.getX() + 0.5, + pos.getY() + 0.5, + pos.getZ() + 0.5, + SoundEvents.CHEST_OPEN, + SoundSource.BLOCKS, + 0.5f, + world.random.nextFloat() * 0.1f + 0.9f + ); + } + + protected void onOpen(@Nullable Level world, BlockPos pos) { + if (world == null) { + return; + } + + world.playSound( + null, + pos.getX() + 0.5, + pos.getY() + 0.5, + pos.getZ() + 0.5, + SoundEvents.CHEST_CLOSE, + SoundSource.BLOCKS, + 0.5f, + world.random.nextFloat() * 0.1f + 0.9f + ); + } + + private void recheckOpen() { + if (!remove) { + openersCounter.recheckOpeners(getLevel(), getBlockPos(), getBlockState()); + } + } + + @Nonnull + @Override + public ItemStack removeItem(int slot, int amount) { + var stack = ContainerHelper.removeItem(getItems(), slot, amount); + if (!stack.isEmpty()) { + setChanged(); + } + return stack; + } + + @Nonnull + @Override + public ItemStack removeItemNoUpdate(int slot) { + return ContainerHelper.takeItem(getItems(), slot); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + ContainerHelper.saveAllItems(nbt, getItems()); + } + + @Override + public void setItem(int slot, ItemStack stack) { + getItems().set(slot, stack); + if (stack.getCount() > getMaxStackSize()) { + stack.setCount(getMaxStackSize()); + } + setChanged(); + } + + @Override + public void startOpen(Player player) { + if (!remove && !player.isSpectator()) { + openersCounter.incrementOpeners(player, getLevel(), getBlockPos(), getBlockState()); + } + } + + @Override + public boolean stillValid(Player player) { + return Container.stillValidBlockEntity(this, player); + } + + @Override + public void stopOpen(Player player) { + if (!remove && !player.isSpectator()) { + openersCounter.decrementOpeners(player, getLevel(), getBlockPos(), getBlockState()); + } + } + + public void tick(Level world, BlockPos ignoredPos, BlockState ignoredState) { + if (world.isClientSide) { + lidController.tickLid(); + } + recheckOpen(); + } + + + @Override + public boolean triggerEvent(int type, int data) { + if (type == EVENT_INTERACTED) { + lidController.shouldBeOpen(data > 0); + return true; + } + + return super.triggerEvent(type, data); + } +} + diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/EnergyCondenserEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/EnergyCondenserEntity.java new file mode 100644 index 0000000..dfe07a4 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/EnergyCondenserEntity.java @@ -0,0 +1,143 @@ +package lv.enes.mc.eris_alchemy.block.entity; + +import jakarta.annotation.Nonnull; +import lv.enes.mc.eris_alchemy.EMC; +import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; +import lv.enes.mc.eris_alchemy.menu.ChestLikeMenu; +import lv.enes.mc.eris_alchemy.menu.EnergyCondenserMenu; +import lv.enes.mc.eris_alchemy.utils.ContainerOpenersCounterUtil; +import lv.enes.mc.eris_alchemy.utils.SyncedValue.SyncedDouble; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.stream.IntStream; + +public class EnergyCondenserEntity extends ChestLikeEntity implements ExtendedScreenHandlerFactory { + private final static int WIDTH = 13; + private final static int HEIGHT = 7; + + private final NonNullList items = NonNullList.withSize(WIDTH * HEIGHT + 1, ItemStack.EMPTY); + + public final SyncedDouble storedEmc = new SyncedDouble(0); + + public EnergyCondenserEntity(BlockPos pos, BlockState state) { + super(ErisAlchemyRegistry.BlockEntities.ENERGY_CONDENSER, pos, state); + } + + @Nonnull + @Override + protected ChestLikeMenu createMenu(int syncId, Inventory playerInventory) { + return new EnergyCondenserMenu(syncId, playerInventory, this); + } + + @Override + protected NonNullList getItems() { + return items; + } + + @Override + protected Block getParent() { + return ErisAlchemyRegistry.Blocks.ENERGY_CONDENSER; + } + + public double getStoredEmc() { + return storedEmc.getValue(); + } + + public SyncedDouble getStoredEmcSyncer() { + return storedEmc; + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + storedEmc.setValue(nbt.getDouble("stored_emc")); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + nbt.putDouble("stored_emc", storedEmc.getValue()); + } + + public void setStoredEmc(double storedEmc) { + this.storedEmc.setValue(storedEmc); + setChanged(); + } + + @Override + public void tick(Level world, BlockPos pos, BlockState state) { + super.tick(world, pos, state); + EMC.getInstance(world).get(items.get(0)).ifPresent(cost -> { + tryConsumeEmc(world, cost); + tryCloneTemplate(cost); + }); + + this.storedEmc.syncIfChanged(ContainerOpenersCounterUtil.getOpeners(openersCounter)); + } + + @Override + public void writeScreenOpeningData(ServerPlayer player, FriendlyByteBuf buf) { + storedEmc.serialize(buf); + } + + private void tryCloneTemplate(double cost) { + if (cost > getStoredEmc()) { + return; + } + + var template = items.get(0); + + items.stream() + .skip(1) // skip template + .filter(stack -> ItemStack.isSameItemSameTags(template, stack)) + .filter(stack -> stack.getCount() < getMaxStackSize() && stack.getCount() < stack.getMaxStackSize()) + .findFirst() + .ifPresentOrElse( + stack -> { + stack.setCount(stack.getCount() + 1); + setStoredEmc(getStoredEmc() - cost); + setChanged(); + }, + () -> IntStream.range(1, items.size()) + .filter(i -> items.get(i).isEmpty()) + .findFirst() + .ifPresent(emptySlot -> { + items.set(emptySlot, template.copyWithCount(1)); + setStoredEmc(getStoredEmc() - cost); + setChanged(); + }) + ); + } + + private void tryConsumeEmc(Level world, double cost) { + if (cost <= getStoredEmc()) { + return; + } + + var emc = EMC.getInstance(world); + var template = items.get(0); + var sacrifice = items.stream() + .skip(1) // skip the template + .filter(stack -> !ItemStack.isSameItemSameTags(template, stack)) + .flatMap(stack -> emc.get(stack) + .stream() + .mapToObj(emcValue -> new StackEmcPair(stack, emcValue))) + .findFirst(); + sacrifice.ifPresent(pair -> { + pair.stack.setCount(pair.stack.getCount() - 1); + setStoredEmc(getStoredEmc() + pair.emc()); + }); + } + + private record StackEmcPair(ItemStack stack, double emc) {} +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/client/ChestLikeScreen.java b/src/main/java/lv/enes/mc/eris_alchemy/client/ChestLikeScreen.java index c98b356..b8967b1 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/client/ChestLikeScreen.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/client/ChestLikeScreen.java @@ -1,13 +1,13 @@ package lv.enes.mc.eris_alchemy.client; -import lv.enes.mc.eris_alchemy.menu.ChestLikeBlockMenu; +import lv.enes.mc.eris_alchemy.menu.ChestLikeMenu; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; -public abstract class ChestLikeScreen extends AbstractContainerScreen { +public abstract class ChestLikeScreen extends AbstractContainerScreen { public ChestLikeScreen(M menu, Inventory inventory, Component title) { super(menu, inventory, title); diff --git a/src/main/java/lv/enes/mc/eris_alchemy/client/EnergyCondenserScreen.java b/src/main/java/lv/enes/mc/eris_alchemy/client/EnergyCondenserScreen.java new file mode 100644 index 0000000..b6762c8 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/client/EnergyCondenserScreen.java @@ -0,0 +1,46 @@ +package lv.enes.mc.eris_alchemy.client; + +import lv.enes.mc.eris_alchemy.EMC; +import lv.enes.mc.eris_alchemy.ErisAlchemy; +import lv.enes.mc.eris_alchemy.menu.EnergyCondenserMenu; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class EnergyCondenserScreen extends ChestLikeScreen { + public static final ResourceLocation TEXTURE = + new ResourceLocation(ErisAlchemy.ID, "textures/gui/container/energy_condenser.png"); + + public static final int TEXTURE_WIDTH = 248; + public static final int TEXTURE_HEIGHT = 237; + + public EnergyCondenserScreen(EnergyCondenserMenu menu, Inventory inventory, Component title) { + super(menu, inventory, title); + } + + @Override + protected ResourceLocation getTexture() { + return TEXTURE; + } + + @Override + protected int getTextureWidth() { + return TEXTURE_WIDTH; + } + + @Override + protected int getTextureHeight() { + return TEXTURE_HEIGHT; + } + + @Override + protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) { + graphics.drawString(font, EMC.formatEmc(menu.getStoredEmc()), 28, 6, 0xFFFFFF); + } + + @Override + protected boolean shouldRenderLabels() { + return false; + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClient.java b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClient.java index 86cc467..a69d2e1 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClient.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClient.java @@ -9,8 +9,6 @@ import org.quiltmc.loader.api.ModContainer; import org.quiltmc.qsl.base.api.entrypoint.client.ClientModInitializer; import org.quiltmc.qsl.tooltip.api.client.ItemTooltipCallback; -import java.text.DecimalFormat; - @SuppressWarnings("unused") public class ErisAlchemyClient implements ClientModInitializer { @Override @@ -19,14 +17,10 @@ public class ErisAlchemyClient implements ClientModInitializer { ErisAlchemyClientRegistry.ItemRenderers.consume(BuiltinItemRendererRegistry.INSTANCE::register); ErisAlchemyClientRegistry.MenuScreens.consume(MenuScreens::register); - // MenuScreens.register(ErisAlchemyRegistry.Menus.ALCHEMICAL_CHEST, AlchemicalChestScreen::new); - - var doubleFormat = new DecimalFormat("0"); - doubleFormat.setMaximumFractionDigits(1); ItemTooltipCallback.EVENT.register((stack, player, context, tooltip) -> { var world = player == null ? null : player.level(); - var emc = EMC.getInstance(world).get(stack.getItem()); - emc.ifPresent(value -> tooltip.add(Component.literal("EMC %s".formatted(doubleFormat.format(value))))); + var emc = EMC.getInstance(world).get(stack); + emc.ifPresent(value -> tooltip.add(Component.literal("EMC %s".formatted(EMC.formatEmc(value))))); }); } } diff --git a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClientRegistry.java b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClientRegistry.java index 3680fd5..30c6690 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClientRegistry.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyClientRegistry.java @@ -1,6 +1,7 @@ package lv.enes.mc.eris_alchemy.client; -import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity; +import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestEntity; +import lv.enes.mc.eris_alchemy.block.entity.EnergyCondenserEntity; import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry.DynamicItemRenderer; import net.minecraft.client.gui.screens.MenuScreens.ScreenConstructor; import net.minecraft.client.gui.screens.Screen; @@ -54,6 +55,7 @@ public final class ErisAlchemyClientRegistry { static { register(BlockEntities.ALCHEMICAL_CHEST, ChestRenderer::new); + register(BlockEntities.ENERGY_CONDENSER, ChestRenderer::new); } } @@ -73,7 +75,11 @@ public final class ErisAlchemyClientRegistry { static { register( Items.ALCHEMICAL_CHEST, - new ChestItemRenderer<>(Blocks.ALCHEMICAL_CHEST, AlchemicalChestBlockEntity::new)::renderByItem + new ChestItemRenderer<>(Blocks.ALCHEMICAL_CHEST, AlchemicalChestEntity::new)::renderByItem + ); + register( + Items.ENERGY_CONDENSER, + new ChestItemRenderer<>(Blocks.ENERGY_CONDENSER, EnergyCondenserEntity::new)::renderByItem ); } } @@ -111,6 +117,7 @@ public final class ErisAlchemyClientRegistry { static { register(Menus.ALCHEMICAL_CHEST, AlchemicalChestScreen::new); + register(Menus.ENERGY_CONDENSER, EnergyCondenserScreen::new); } } } diff --git a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyMaterials.java b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyMaterials.java index 15d9209..686d83f 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyMaterials.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/client/ErisAlchemyMaterials.java @@ -11,5 +11,10 @@ public final class ErisAlchemyMaterials { new ResourceLocation(ErisAlchemy.ID, "entity/chest/alchemical_chest") ); + public static final Material ENERGY_CONDENSER = new Material( + Sheets.CHEST_SHEET, + new ResourceLocation(ErisAlchemy.ID, "entity/chest/energy_condenser") + ); + private ErisAlchemyMaterials() {} } diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java index 1dc3364..35db476 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java @@ -6,7 +6,7 @@ import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.Slot; -public class AlchemicalChestMenu extends ChestLikeBlockMenu { +public class AlchemicalChestMenu extends ChestLikeMenu { private static final int WIDTH = 13; private static final int HEIGHT = 8; @@ -24,25 +24,13 @@ public class AlchemicalChestMenu extends ChestLikeBlockMenu { var y_off = 8; for (var y = 0; y < HEIGHT; y++) { - for (var x = 0; x < WIDTH; x++ ) { + for (var x = 0; x < WIDTH; x++) { addSlot(new Slot(container, y * WIDTH + x, x_off + x * 18, y_off + y * 18)); } } - x_off = 44; - y_off = 155; - - for (var y = 0; y < 3; y++) { - for (var x = 0; x < 9; x++) { - addSlot(new Slot(playerInventory, y * 9 + x + 9, x_off + x * 18, y_off + y * 18)); - } - } - - y_off = 213; - - for (var x = 0; x < 9; x++) { - addSlot(new Slot(playerInventory, x, x_off + x * 18, y_off)); - } + addPlayerInventorySlots(playerInventory, 44, 155); + addPlayerHotbarSlots(playerInventory, 44, 213); } @Override diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java deleted file mode 100644 index 897abe9..0000000 --- a/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java +++ /dev/null @@ -1,67 +0,0 @@ -package lv.enes.mc.eris_alchemy.menu; - -import jakarta.annotation.Nonnull; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.item.ItemStack; - -public abstract class ChestLikeBlockMenu extends AbstractContainerMenu { - protected final Container container; - - public ChestLikeBlockMenu( - MenuType type, - int syncId, - Inventory playerInventory, - Container container - ) { - super(type, syncId); - checkContainerSize(container, getRequiredSize()); - this.container = container; - container.startOpen(playerInventory.player); - addSlots(playerInventory); - } - - protected abstract void addSlots(Inventory playerInventory); - protected abstract int getRequiredSize(); - - public Container getContainer() { - return container; - } - - @Nonnull - @Override - public ItemStack quickMoveStack(Player player, int fromIndex) { - var newStack = ItemStack.EMPTY; - var slot = slots.get(fromIndex); - if (!slot.hasItem()) { - return newStack; - } - - var originalStack = slot.getItem(); - newStack = originalStack.copy(); - - if (fromIndex < container.getContainerSize()) { - if (!moveItemStackTo(originalStack, container.getContainerSize(), slots.size(), true)) { - return ItemStack.EMPTY; - } - } else if (!moveItemStackTo(originalStack, 0, container.getContainerSize(), false)) { - return ItemStack.EMPTY; - } - - if (originalStack.isEmpty()) { - slot.setByPlayer(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - - return newStack; - } - - @Override - public boolean stillValid(Player player) { - return container.stillValid(player); - } -} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeMenu.java new file mode 100644 index 0000000..e561f0d --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeMenu.java @@ -0,0 +1,101 @@ +package lv.enes.mc.eris_alchemy.menu; + +import jakarta.annotation.Nonnull; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +public abstract class ChestLikeMenu extends AbstractContainerMenu { + protected final Container container; + + public ChestLikeMenu( + MenuType type, + int syncId, + Inventory playerInventory, + Container container + ) { + super(type, syncId); + checkContainerSize(container, getRequiredSize()); + this.container = container; + container.startOpen(playerInventory.player); + addSlots(playerInventory); + } + + protected abstract void addSlots(Inventory playerInventory); + protected abstract int getRequiredSize(); + + protected void addPlayerInventorySlots(Inventory playerInventory, int xOff, int yOff) { + for (var y = 0; y < 3; y++) { + for (var x = 0; x < 9; x++) { + addSlot(new Slot(playerInventory, y * 9 + x + 9, xOff + x * 18, yOff + y * 18)); + } + } + } + + protected void addPlayerHotbarSlots(Inventory playerInventory, int xOff, int yOff) { + for (var x = 0; x < 9; x++) { + addSlot(new Slot(playerInventory, x, xOff + x * 18, yOff)); + } + } + + public Container getContainer() { + return container; + } + + protected int getQuickMoveStart() { + return 0; + } + + protected int getQuickMoveEnd() { + return container.getContainerSize(); + } + + @Override + public void broadcastChanges() { + super.broadcastChanges(); + } + + @Override + public void broadcastFullState() { + super.broadcastFullState(); + } + + @Nonnull + @Override + public ItemStack quickMoveStack(Player player, int fromIndex) { + var newStack = ItemStack.EMPTY; + var slot = slots.get(fromIndex); + if (!slot.hasItem()) { + return newStack; + } + + var originalStack = slot.getItem(); + newStack = originalStack.copy(); + + if (fromIndex < container.getContainerSize()) { + // In container, else player inv + if (!moveItemStackTo(originalStack, container.getContainerSize(), slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!moveItemStackTo(originalStack, getQuickMoveStart(), getQuickMoveEnd(), false)) { + return ItemStack.EMPTY; + } + + if (originalStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return newStack; + } + + @Override + public boolean stillValid(Player player) { + return container.stillValid(player); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/EnergyCondenserMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/EnergyCondenserMenu.java new file mode 100644 index 0000000..bdba77b --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/menu/EnergyCondenserMenu.java @@ -0,0 +1,60 @@ +package lv.enes.mc.eris_alchemy.menu; + +import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; +import lv.enes.mc.eris_alchemy.block.entity.EnergyCondenserEntity; +import lv.enes.mc.eris_alchemy.utils.SyncedValue.SyncedDouble; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.Slot; + +public class EnergyCondenserMenu extends ChestLikeMenu { + private static final int WIDTH = 13; + private static final int HEIGHT = 7; + + private final SyncedDouble storedEmc; + + public EnergyCondenserMenu(int syncId, Inventory playerInventory, FriendlyByteBuf buf) { + this(syncId, playerInventory, new SimpleContainer(WIDTH * HEIGHT + 1), SyncedDouble.deserialize(buf)); + } + + public EnergyCondenserMenu(int syncId, Inventory playerInventory, EnergyCondenserEntity entity) { + this(syncId, playerInventory, entity, entity.getStoredEmcSyncer()); + } + + public EnergyCondenserMenu(int syncId, Inventory playerInventory, Container container, SyncedDouble storedEmc) { + super(ErisAlchemyRegistry.Menus.ENERGY_CONDENSER, syncId, playerInventory, container); + this.storedEmc = storedEmc; + } + + @Override + protected void addSlots(Inventory playerInventory) { + addSlot(new Slot(container, 0, 8, 7)); + + var xOff = 8; + var yOff = 28; + for (var y = 0; y < HEIGHT; y++) { + for (var x = 0; x < WIDTH; x++) { + addSlot(new Slot(container, y * WIDTH + x + 1, xOff + x * 18, yOff + y * 18)); + } + } + + addPlayerInventorySlots(playerInventory, 44, 157); + addPlayerHotbarSlots(playerInventory, 44, 213); + } + + @Override + protected int getQuickMoveStart() { + return 1; + } + + @Override + protected int getRequiredSize() { + return WIDTH * HEIGHT + 1; + } + + public double getStoredEmc() { + return storedEmc.getValue(); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/mixin/ContainerOpenersCounterMixin.java b/src/main/java/lv/enes/mc/eris_alchemy/mixin/ContainerOpenersCounterMixin.java new file mode 100644 index 0000000..13b8669 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/mixin/ContainerOpenersCounterMixin.java @@ -0,0 +1,56 @@ +package lv.enes.mc.eris_alchemy.mixin; + +import jakarta.annotation.Nonnull; +import lv.enes.mc.eris_alchemy.utils.ContainerOpenersCounterUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.ContainerOpenersCounter; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.phys.AABB; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +@Mixin(ContainerOpenersCounter.class) +public abstract class ContainerOpenersCounterMixin + implements ContainerOpenersCounterUtil.ContainerOpenersCounterSuper { + @Shadow protected abstract boolean isOwnContainer(Player player); + + @Unique + private final static Map> openers = new WeakHashMap<>(); + + @Inject(method = "recheckOpeners", at = @At("RETURN")) + public void onRecheckOpeners(Level world, BlockPos pos, BlockState state, CallbackInfo ci) { + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + var boundingBox = new AABB( + x - 5.0, y - 5.0, z - 5.0, + x + 5.0, y + 5.0, z + 5.0 + ); + var players = world.getEntities(EntityTypeTest.forClass(ServerPlayer.class), boundingBox, this::isOwnContainer); + if (openers.containsKey(this)) { + var list = openers.get(this); + list.clear(); + list.addAll(players); + } else { + openers.put(this, players); + } + } + + @Nonnull + @Override + public List lv_enes_mc$getOpeners() { + return openers.getOrDefault(this, List.of()); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/mixin/client/SheetsMixin.java b/src/main/java/lv/enes/mc/eris_alchemy/mixin/client/SheetsMixin.java index 46f7720..145a68c 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/mixin/client/SheetsMixin.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/mixin/client/SheetsMixin.java @@ -1,7 +1,8 @@ package lv.enes.mc.eris_alchemy.mixin.client; +import lv.enes.mc.eris_alchemy.block.entity.EnergyCondenserEntity; import lv.enes.mc.eris_alchemy.client.ErisAlchemyMaterials; -import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity; +import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestEntity; import net.minecraft.client.renderer.Sheets; import net.minecraft.client.resources.model.Material; import net.minecraft.world.level.block.entity.BlockEntity; @@ -24,8 +25,10 @@ public abstract class SheetsMixin { boolean christmas, CallbackInfoReturnable cir ) { - if (entity instanceof AlchemicalChestBlockEntity) { + if (entity instanceof AlchemicalChestEntity) { cir.setReturnValue(ErisAlchemyMaterials.ALCHEMICAL_CHEST); + } else if (entity instanceof EnergyCondenserEntity) { + cir.setReturnValue(ErisAlchemyMaterials.ENERGY_CONDENSER); } } } diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/ContainerOpenersCounterUtil.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/ContainerOpenersCounterUtil.java new file mode 100644 index 0000000..abedce7 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/ContainerOpenersCounterUtil.java @@ -0,0 +1,20 @@ +package lv.enes.mc.eris_alchemy.utils; + +import jakarta.annotation.Nonnull; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.entity.ContainerOpenersCounter; + +import java.util.List; + +public final class ContainerOpenersCounterUtil { + private ContainerOpenersCounterUtil() {} + + /** @see lv.enes.mc.eris_alchemy.mixin.ContainerOpenersCounterMixin */ + public interface ContainerOpenersCounterSuper { + @Nonnull List lv_enes_mc$getOpeners(); + } + + public static List getOpeners(ContainerOpenersCounter cop) { + return ((ContainerOpenersCounterSuper)cop).lv_enes_mc$getOpeners(); + } +} diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/SyncedValue.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/SyncedValue.java new file mode 100644 index 0000000..c7e52bf --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/SyncedValue.java @@ -0,0 +1,115 @@ +package lv.enes.mc.eris_alchemy.utils; + +import jakarta.annotation.Nonnull; +import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; +import net.fabricmc.api.EnvType; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import org.quiltmc.loader.api.minecraft.MinecraftQuiltLoader; +import org.quiltmc.qsl.networking.api.PacketByteBufs; +import org.quiltmc.qsl.networking.api.ServerPlayNetworking; +import org.quiltmc.qsl.networking.api.client.ClientPlayNetworking; + +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public sealed abstract class SyncedValue { + private final static Map>> values = new HashMap<>(); + + private final long id; + private boolean changed = true; + private E value; + + public SyncedValue(E value) { + this(genId(), value); + } + + public SyncedValue(long id, E value) { + this.id = id; + this.value = value; + + values.put(this.id, new WeakReference<>(this)); + } + + protected abstract @Nonnull E read(FriendlyByteBuf buf); + protected abstract void write(FriendlyByteBuf buf, @Nonnull E value); + + public void serialize(FriendlyByteBuf buf) { + buf.writeLongLE(id); + write(buf, value); + } + + public E getValue() { + return value; + } + + public void setValue(E value) { + this.value = value; + this.changed = true; + } + + public void syncIfChanged(Collection players) { + if (!changed) { + return; + } + var buf = PacketByteBufs.create(); + serialize(buf); + ServerPlayNetworking.send(players, ErisAlchemyRegistry.NetworkingConstants.UPDATE_SYNCED_VALUE, buf); + changed = false; + } + + private void readData(FriendlyByteBuf buf) { + this.changed = false; + this.value = read(buf); + } + + private static long nextId = 0; + private static long genId() { + return nextId++; + } + + static { + if (MinecraftQuiltLoader.getEnvironmentType() == EnvType.CLIENT) { + ClientPlayNetworking.registerGlobalReceiver( + ErisAlchemyRegistry.NetworkingConstants.UPDATE_SYNCED_VALUE, + (client, handler, buf, responseSender) -> { + var id = buf.readLongLE(); + var ref = values.get(id); + var holder = ref == null ? null : ref.get(); + if (holder != null) { + holder.readData(buf); + } + } + ); + } + } + + public static final class SyncedDouble extends SyncedValue { + public SyncedDouble(double value) { + super(value); + } + + public SyncedDouble(long id, double value) { + super(id, value); + } + + public static SyncedDouble deserialize(FriendlyByteBuf buf) { + var id = buf.readLongLE(); + var value = buf.readDoubleLE(); + return new SyncedDouble(id, value); + } + + @Nonnull + @Override + protected Double read(FriendlyByteBuf buf) { + return buf.readDoubleLE(); + } + + @Override + protected void write(FriendlyByteBuf buf, @Nonnull Double value) { + buf.writeDoubleLE(value); + } + } +} diff --git a/src/main/resources/assets/eris_alchemy/blockstates/energy_condenser.json b/src/main/resources/assets/eris_alchemy/blockstates/energy_condenser.json new file mode 100644 index 0000000..a6fc12f --- /dev/null +++ b/src/main/resources/assets/eris_alchemy/blockstates/energy_condenser.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "eris_alchemy:block/energy_condenser" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/eris_alchemy/lang/en_us.json b/src/main/resources/assets/eris_alchemy/lang/en_us.json index ed007dd..c399a68 100644 --- a/src/main/resources/assets/eris_alchemy/lang/en_us.json +++ b/src/main/resources/assets/eris_alchemy/lang/en_us.json @@ -1,11 +1,13 @@ { "block.eris_alchemy.alchemical_chest": "Alchemical Chest", + "block.eris_alchemy.energy_condenser": "Energy Condenser", "book.eris_alchemy.title": "Eris Alchemy", "book.eris_alchemy.subtitle": "the Official Guide", "book.eris_alchemy.landing_text": "Welcome to Alchemy!", "container.eris_alchemy.alchemical_chest": "Alchemical Chest", + "container.eris_alchemy.energy_condenser": "Energy Condenser", "item.eris_alchemy.low_covalence_dust": "Low Covalence Dust", "item.eris_alchemy.medium_covalence_dust": "Medium Covalence Dust", diff --git a/src/main/resources/assets/eris_alchemy/models/block/energy_condenser.json b/src/main/resources/assets/eris_alchemy/models/block/energy_condenser.json new file mode 100644 index 0000000..84acbec --- /dev/null +++ b/src/main/resources/assets/eris_alchemy/models/block/energy_condenser.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/stone" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/eris_alchemy/models/item/energy_condenser.json b/src/main/resources/assets/eris_alchemy/models/item/energy_condenser.json new file mode 100644 index 0000000..62b3156 --- /dev/null +++ b/src/main/resources/assets/eris_alchemy/models/item/energy_condenser.json @@ -0,0 +1,6 @@ +{ + "parent": "item/chest", + "textures": { + "particle": "block/stone" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/covalence_dusts.json b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/covalence_dusts.json index a55ae50..6000bdc 100644 --- a/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/covalence_dusts.json +++ b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/covalence_dusts.json @@ -4,7 +4,6 @@ "category": "eris_alchemy:root", "pages": [{ "type": "patchouli:spotlight", - "title": "Covalence Dusts", "item": "eris_alchemy:low_covalence_dust,eris_alchemy:medium_covalence_dust,eris_alchemy:high_covalence_dust", "link_recipe": true, "text": "$(thing)Covalence dusts$() are the most basic items you create in Eris Alchemy." diff --git a/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/energy_condenser.json b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/energy_condenser.json new file mode 100644 index 0000000..cea3e75 --- /dev/null +++ b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/energy_condenser.json @@ -0,0 +1,14 @@ +{ + "name": "Energy Condenser", + "icon": "eris_alchemy:energy_condenser", + "category": "eris_alchemy:root", + "pages": [{ + "type": "patchouli:spotlight", + "item": "eris_alchemy:energy_condenser", + "link_recipe": true, + "text": "$(item)Energy Condenser$() has a slightly smaller chest space than $(item)Alchemical Chest$() but it transforms items placed into it into the template item (placed in the upper left slot)." + }, { + "type": "patchouli:crafting", + "recipe": "eris_alchemy:energy_condenser" + }] +} \ No newline at end of file diff --git a/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/guide_book.json b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/guide_book.json index b501b0b..ba0812b 100644 --- a/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/guide_book.json +++ b/src/main/resources/assets/eris_alchemy/patchouli_books/guide_book/en_us/entries/root/guide_book.json @@ -4,7 +4,6 @@ "category": "eris_alchemy:root", "pages": [{ "type": "patchouli:spotlight", - "title": "Guide Book", "item": "patchouli:guide_book{'patchouli:book':'eris_alchemy:guide_book'}", "link_recipe": true, "text": "The Eris Alchemy $(item)Guide Book$() should be already in your inventory for obvious reasons, but if you ever lose it you can recreate it with this crafting recipe." diff --git a/src/main/resources/assets/eris_alchemy/textures/entity/chest/energy_condenser.png b/src/main/resources/assets/eris_alchemy/textures/entity/chest/energy_condenser.png new file mode 100644 index 0000000..4501c59 Binary files /dev/null and b/src/main/resources/assets/eris_alchemy/textures/entity/chest/energy_condenser.png differ diff --git a/src/main/resources/assets/eris_alchemy/textures/gui/container/energy_condenser.png b/src/main/resources/assets/eris_alchemy/textures/gui/container/energy_condenser.png new file mode 100644 index 0000000..d075bed Binary files /dev/null and b/src/main/resources/assets/eris_alchemy/textures/gui/container/energy_condenser.png differ diff --git a/src/main/resources/data/eris_alchemy/loot_tables/blocks/energy_condenser.json b/src/main/resources/data/eris_alchemy/loot_tables/blocks/energy_condenser.json new file mode 100644 index 0000000..07e2f55 --- /dev/null +++ b/src/main/resources/data/eris_alchemy/loot_tables/blocks/energy_condenser.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "eris_alchemy:energy_condenser", + "functions": [{ + "function": "minecraft:copy_nbt", + "source": "block_entity", + "ops": [{ + "op": "replace", + "source": "stored_emc", + "target": "BlockEntityTag.stored_emc" + }] + }] + }], + "conditions": [{ + "condition": "minecraft:survives_explosion" + }] + }] +} \ No newline at end of file diff --git a/src/main/resources/data/eris_alchemy/recipes/energy_condenser.json b/src/main/resources/data/eris_alchemy/recipes/energy_condenser.json new file mode 100644 index 0000000..55c04bf --- /dev/null +++ b/src/main/resources/data/eris_alchemy/recipes/energy_condenser.json @@ -0,0 +1,22 @@ +{ + "type": "crafting_shaped", + "pattern": [ + "ODO", + "DCD", + "ODO" + ], + "key": { + "O": { + "item": "obsidian" + }, + "D": { + "item": "diamond" + }, + "C": { + "item": "eris_alchemy:alchemical_chest" + } + }, + "result": { + "item": "eris_alchemy:energy_condenser" + } +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index 27c0b6d..958cc31 100644 --- a/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -1,6 +1,7 @@ { "replace": false, "values": [ - "eris_alchemy:alchemical_chest" + "eris_alchemy:alchemical_chest", + "eris_alchemy:energy_condenser" ] } \ No newline at end of file diff --git a/src/main/resources/eris_alchemy.mixins.json b/src/main/resources/eris_alchemy.mixins.json index 36cb47f..fb52b89 100644 --- a/src/main/resources/eris_alchemy.mixins.json +++ b/src/main/resources/eris_alchemy.mixins.json @@ -7,6 +7,7 @@ "client.SheetsMixin" ], "mixins": [ + "ContainerOpenersCounterMixin", "CoralBlockMixin", "CoralFanBlockMixin", "CoralPlantBlockMixin", diff --git a/src/main/resources/quilt.mod.json b/src/main/resources/quilt.mod.json index 5b6373a..4e5a88d 100644 --- a/src/main/resources/quilt.mod.json +++ b/src/main/resources/quilt.mod.json @@ -6,15 +6,10 @@ "version": "${version}", "metadata": { "name": "Eris Alchemy", - "description": "Adds the condensing chest from EE2.", + "description": "Adds the energy condenser (and a bit more) from EE2.", "contributors": { "Eris": "Owner" }, - "contact": { - "homepage": "https://TODO", - "issues": "https://TODO", - "sources": "https://TODO" - }, "icon": "assets/eris_alchemy/icon.png" }, "intermediate_mappings": "net.fabricmc:intermediary", -- cgit v1.2.3