diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index 25abef90..2ee36603 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,4 +1,4 @@ -// 1.20.1 2024-01-10T20:18:25.7260241 Loot Tables +// 1.20.1 2024-01-25T14:16:46.237861 Loot Tables 105d8a61ea7145d7798146d385d4aad24fd1588d data/exdeorum/loot_tables/blocks/acacia_barrel.json 1e77127a82cbba0937bb02694f65cf1893aeffcb data/exdeorum/loot_tables/blocks/acacia_crucible.json fcc00910a8cc94bed6339d6833fcec53c501a0d7 data/exdeorum/loot_tables/blocks/acacia_sieve.json @@ -73,6 +73,7 @@ b38104ee25127d9c65ad9e323ed879f76df7a048 data/exdeorum/loot_tables/blocks/mangro 475b89fd8f09834652f80c93d8a6d0964d708ead data/exdeorum/loot_tables/blocks/maple_barrel.json 54f36187d7fb97dedc4680d14e2ad7d70b5c64af data/exdeorum/loot_tables/blocks/maple_crucible.json 7ffe80360af055f3977d05b5684a299886bcb756 data/exdeorum/loot_tables/blocks/maple_sieve.json +926a9224e747bf53eb81448c1abb55d73fce5092 data/exdeorum/loot_tables/blocks/mechanical_hammer.json a84508222cb36b07cb20ee31915d802bcc411149 data/exdeorum/loot_tables/blocks/mechanical_sieve.json cad973c873a2e50ccfac91e88eadb3c2462d39d1 data/exdeorum/loot_tables/blocks/oak_barrel.json f94bc97efbfd26ccf7dba32d414fb5e33decd5f6 data/exdeorum/loot_tables/blocks/oak_crucible.json diff --git a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 index e227d341..6237fdac 100644 --- a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 +++ b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 @@ -1,2 +1,2 @@ -// 1.20.1 2024-01-26T16:29:51.8613552 ModKit Language: en_us for mod 'exdeorum' -2f38e208308fb2a7f506e2ce2b400970d1c27b57 assets/exdeorum/lang/en_us.json +// 1.20.1 2024-01-25T14:16:46.2316734 ModKit Language: en_us for mod 'exdeorum' +248ab1a837b9857fb1928e826cb02f9f142e8d96 assets/exdeorum/lang/en_us.json diff --git a/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd b/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd index 57425b7f..f3d7bef9 100644 --- a/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd +++ b/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd @@ -1,4 +1,4 @@ -// 1.20.1 2024-01-10T20:18:25.7240245 ModKit Item Models for mod 'exdeorum' +// 1.20.1 2024-01-25T14:16:46.2347562 ModKit Item Models for mod 'exdeorum' 4ba3bb2c6174ac3728a4b85e34681f118ec8eb34 assets/exdeorum/models/item/acacia_barrel.json c03ce41f7c071498fcbd5f5225e91dcb2f365fbb assets/exdeorum/models/item/acacia_crucible.json 3b4f1d45c0d9c4cd1d9a5cdf6ddc8d2c9791bca5 assets/exdeorum/models/item/acacia_sieve.json @@ -105,6 +105,7 @@ ff89dc05408074da0e9d41bfef91dfe975302403 assets/exdeorum/models/item/mahogany_cr c3f2af2a88cd97148b05efbd6e24fc2558fcc0b8 assets/exdeorum/models/item/maple_barrel.json cc045825c562e9133858ce5cfe6e6f1dcb747d8e assets/exdeorum/models/item/maple_crucible.json a64e9b9ce91ac6b2f36690a770afc52b8900a614 assets/exdeorum/models/item/maple_sieve.json +59cdfe2c26f9d4a16ab7e8e7a87c97403eafa562 assets/exdeorum/models/item/mechanical_hammer.json 1e2b482f5fc4d283f5ca12919b575f86dc4a9541 assets/exdeorum/models/item/mechanical_sieve.json d543d3e18bdcf2bf79a762b52cc61a4161124db1 assets/exdeorum/models/item/mycelium_spores.json 1f48b2ce3452ce5d02142c9f663ae7bdb7d1d934 assets/exdeorum/models/item/netherite_hammer.json diff --git a/src/generated/resources/assets/exdeorum/lang/en_us.json b/src/generated/resources/assets/exdeorum/lang/en_us.json index 61fffc83..1edbc801 100644 --- a/src/generated/resources/assets/exdeorum/lang/en_us.json +++ b/src/generated/resources/assets/exdeorum/lang/en_us.json @@ -84,6 +84,7 @@ "block.exdeorum.maple_barrel": "Maple Barrel", "block.exdeorum.maple_crucible": "Maple Crucible", "block.exdeorum.maple_sieve": "Maple Sieve", + "block.exdeorum.mechanical_hammer": "Mechanical Hammer", "block.exdeorum.mechanical_sieve": "Mechanical Sieve", "block.exdeorum.oak_barrel": "Oak Barrel", "block.exdeorum.oak_crucible": "Oak Crucible", @@ -118,6 +119,7 @@ "block.exdeorum.willow_crucible": "Willow Crucible", "block.exdeorum.willow_sieve": "Willow Sieve", "block.exdeorum.witch_water": "Witch Water", + "exdeorum.container.mechanical_hammer": "Mechanical Hammer", "exdeorum.container.mechanical_sieve": "Mechanical Sieve", "fluid_type.exdeorum.witch_water": "Witch Water", "generator.exdeorum.void_world": "Void World", @@ -145,6 +147,7 @@ "gui.exdeorum.redstone_control.unpowered": "Unpowered", "info.exdeorum.crimson_nylium_spores": "Use on netherrack to turn it into a crimson nylium block.", "info.exdeorum.grass_seeds": "Use on dirt to turn it into a grass block.", + "info.exdeorum.mechanical_hammer": "The Mechanical Hammer is a machine that, when supplied with Forge Energy (FE), will hammer blocks without a player having to do it themselves. It can operate without a hammer, but adding any hammer will double the speed, and efficiency enchantments on the hammer will further increase speed. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power.", "info.exdeorum.mechanical_sieve": "The Mechanical Sieve is a machine that, when supplied with a mesh and Forge Energy (FE), will sift blocks without a player having to do it themselves. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power.", "info.exdeorum.mycelium_spores": "Use on dirt to turn it into mycelium.", "info.exdeorum.sculk_core": "Use a sculk core on a Sculk Shrieker to enable it to spawn Wardens. Normally, Sculk Shriekers placed by players cannot spawn Wardens, so this item is useful for obtaining Sculk items in a SkyBlock world.", diff --git a/src/generated/resources/assets/exdeorum/models/item/mechanical_hammer.json b/src/generated/resources/assets/exdeorum/models/item/mechanical_hammer.json new file mode 100644 index 00000000..a35353c0 --- /dev/null +++ b/src/generated/resources/assets/exdeorum/models/item/mechanical_hammer.json @@ -0,0 +1,3 @@ +{ + "parent": "exdeorum:block/mechanical_hammer_off" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_hammer.json b/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_hammer.json new file mode 100644 index 00000000..376331ae --- /dev/null +++ b/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_hammer.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:hopper" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "exdeorum:mechanical_hammer" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_item", + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "exdeorum:mechanical_hammer" + ] + }, + "sends_telemetry_event": true +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_hammer.json b/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_hammer.json new file mode 100644 index 00000000..674d4b46 --- /dev/null +++ b/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_hammer.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "exdeorum:machine" + } + ], + "name": "exdeorum:mechanical_hammer" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "exdeorum:blocks/mechanical_hammer" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/mechanical_hammer.json b/src/generated/resources/data/exdeorum/recipes/mechanical_hammer.json new file mode 100644 index 00000000..8313165b --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/mechanical_hammer.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "#": { + "item": "minecraft:iron_block" + }, + "H": { + "item": "minecraft:hopper" + }, + "I": { + "item": "minecraft:iron_ingot" + }, + "T": { + "tag": "exdeorum:hammers" + } + }, + "pattern": [ + "III", + "ITI", + "#H#" + ], + "result": { + "item": "exdeorum:mechanical_hammer" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/java/thedarkcolour/exdeorum/block/MechanicalHammerBlock.java b/src/main/java/thedarkcolour/exdeorum/block/MechanicalHammerBlock.java new file mode 100644 index 00000000..dd1565c0 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/block/MechanicalHammerBlock.java @@ -0,0 +1,133 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.block; + +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +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.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraftforge.items.ItemStackHandler; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.blockentity.MechanicalHammerBlockEntity; +import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; +import thedarkcolour.exdeorum.registry.EBlockEntities; + +import java.util.List; + +public class MechanicalHammerBlock extends EBlock { + public static final BooleanProperty RUNNING = BooleanProperty.create("running"); + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + + public MechanicalHammerBlock(Properties properties) { + super(properties, EBlockEntities.MECHANICAL_HAMMER); + + registerDefaultState(defaultBlockState().setValue(RUNNING, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(RUNNING, FACING); + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) { + return type == EBlockEntities.MECHANICAL_HAMMER.get() && !level.isClientSide ? (BlockEntityTicker) new MechanicalHammerBlockEntity.ServerTicker<>() : null; + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, TooltipFlag flag) { + var nbt = BlockItem.getBlockEntityData(stack); + if (nbt != null) { + var inventoryNbt = nbt.getCompound("inventory"); + var inventory = new ItemStackHandler(); + inventory.deserializeNBT(inventoryNbt); + var hammer = inventory.getStackInSlot(MechanicalHammerBlockEntity.HAMMER_SLOT); + if (!hammer.isEmpty()) { + tooltip.add(Component.translatable(TranslationKeys.MECHANICAL_HAMMER_HAMMER_LABEL).withStyle(ChatFormatting.GRAY).append(Component.translatable(hammer.getDescriptionId()))); + } + var energy = nbt.getInt("energy"); + tooltip.add(Component.translatable(TranslationKeys.ENERGY).withStyle(ChatFormatting.GRAY).append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, energy, EConfig.SERVER.mechanicalSieveEnergyStorage.get())).append(" FE")); + } + } + + @Override + public void playerWillDestroy(Level level, BlockPos pos, BlockState pState, Player player) { + if (!level.isClientSide && player.isCreative() && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) { + if (level.getBlockEntity(pos) instanceof MechanicalHammerBlockEntity sieve) { + if (!sieve.inventory.getStackInSlot(MechanicalHammerBlockEntity.HAMMER_SLOT).isEmpty()) { + var stack = new ItemStack(this); + BlockItem.setBlockEntityData(stack, EBlockEntities.MECHANICAL_HAMMER.get(), sieve.saveWithoutMetadata()); + var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, stack); + itemEntity.setDefaultPickUpDelay(); + level.addFreshEntity(itemEntity); + } + } + } + + super.playerWillDestroy(level, pos, pState, player); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + var nbt = BlockItem.getBlockEntityData(context.getItemInHand()); + var state = defaultBlockState(); + if (nbt != null && nbt.contains("progress") && nbt.getInt("progress") != MechanicalHammerBlockEntity.NOT_RUNNING) { + state = state.setValue(RUNNING, true); + } + + return state.setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { + if (!oldState.is(state.getBlock())) { + if (level.getBlockEntity(pos) instanceof MechanicalHammerBlockEntity hammer) { + hammer.checkPoweredState(level, pos); + } + } + } + + @Override + public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if (level.getBlockEntity(pos) instanceof MechanicalHammerBlockEntity hammer) { + hammer.checkPoweredState(level, pos); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java b/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java index da3e56c3..48f7ca4f 100644 --- a/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java +++ b/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java @@ -69,7 +69,7 @@ public class MechanicalSieveBlock extends EBlock { @Nullable @Override public BlockEntityTicker getTicker(Level level, BlockState pState, BlockEntityType type) { - return type == EBlockEntities.MECHANICAL_SIEVE.get() && !level.isClientSide ? (BlockEntityTicker) new MechanicalSieveBlockEntity.ServerTicker() : null; + return type == EBlockEntities.MECHANICAL_SIEVE.get() && !level.isClientSide ? (BlockEntityTicker) new MechanicalSieveBlockEntity.ServerTicker<>() : null; } @Override @@ -93,7 +93,7 @@ public class MechanicalSieveBlock extends EBlock { public void playerWillDestroy(Level level, BlockPos pos, BlockState pState, Player player) { if (!level.isClientSide && player.isCreative() && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) { if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) { - if (!sieve.getMesh().isEmpty()) { + if (!sieve.getLogic().getMesh().isEmpty()) { var stack = new ItemStack(this); BlockItem.setBlockEntityData(stack, EBlockEntities.MECHANICAL_SIEVE.get(), sieve.saveWithoutMetadata()); var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, stack); diff --git a/src/main/java/thedarkcolour/exdeorum/block/SieveBlock.java b/src/main/java/thedarkcolour/exdeorum/block/SieveBlock.java index 37efda92..b85ca338 100644 --- a/src/main/java/thedarkcolour/exdeorum/block/SieveBlock.java +++ b/src/main/java/thedarkcolour/exdeorum/block/SieveBlock.java @@ -57,7 +57,7 @@ public class SieveBlock extends EBlock { if (!level.isClientSide) { if (!state.is(newState.getBlock())) { if (level.getBlockEntity(pos) instanceof SieveBlockEntity sieve) { - var mesh = sieve.getMesh(); + var mesh = sieve.getLogic().getMesh(); if (!mesh.isEmpty()) { dropItem(level, pos, mesh); diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractMachineBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractMachineBlockEntity.java new file mode 100644 index 00000000..3007c4d4 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractMachineBlockEntity.java @@ -0,0 +1,174 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +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.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.energy.EnergyStorage; +import net.minecraftforge.network.NetworkHooks; +import thedarkcolour.exdeorum.blockentity.helper.EnergyHelper; +import thedarkcolour.exdeorum.blockentity.helper.ItemHelper; +import thedarkcolour.exdeorum.client.screen.RedstoneControlWidget; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +public abstract class AbstractMachineBlockEntity> extends EBlockEntity implements MenuProvider { + public final ItemHelper inventory; + public final EnergyHelper energy; + protected int redstoneMode; + // not saved to NBT + protected boolean hasRedstonePower; + + private final LazyOptional capabilityInventory; + private final LazyOptional capabilityEnergy; + + @SuppressWarnings("unchecked") + public AbstractMachineBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, Function inventory, int maxEnergy) { + super(type, pos, state); + + this.inventory = inventory.apply((M) this); + this.energy = new EnergyHelper(maxEnergy); + + this.capabilityInventory = LazyOptional.of(() -> this.inventory); + this.capabilityEnergy = LazyOptional.of(() -> this.energy); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + + nbt.put("inventory", this.inventory.serializeNBT()); + nbt.putInt("energy", this.energy.getEnergyStored()); + nbt.putInt("redstoneMode", this.redstoneMode); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + + this.inventory.deserializeNBT(nbt.getCompound("inventory")); + this.energy.setStoredEnergy(nbt.getInt("energy")); + this.redstoneMode = Mth.clamp(nbt.getInt("redstoneMode"), 0, 2); + } + + @Override + public void onLoad() { + checkPoweredState(this.level, this.worldPosition); + } + + public void checkPoweredState(Level level, BlockPos pos) { + this.hasRedstonePower = level.hasNeighborSignal(pos); + } + + public void setRedstoneMode(int redstoneMode) { + this.redstoneMode = redstoneMode; + } + + public int getRedstoneMode() { + return this.redstoneMode; + } + + + @SuppressWarnings("NullableProblems") + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @javax.annotation.Nullable Direction side) { + if (cap == ForgeCapabilities.ENERGY) { + return this.capabilityEnergy.cast(); + } else if (cap == ForgeCapabilities.ITEM_HANDLER) { + return this.capabilityInventory.cast(); + } + + return super.getCapability(cap, side); + } + + @Override + public void invalidateCaps() { + super.invalidateCaps(); + + this.capabilityEnergy.invalidate(); + this.capabilityInventory.invalidate(); + } + + @Override + public InteractionResult use(Level level, Player player, InteractionHand hand) { + if (player instanceof ServerPlayer serverPlayer) { + NetworkHooks.openScreen(serverPlayer, this, buffer -> { + buffer.writeBlockPos(getBlockPos()); + buffer.writeByte(this.redstoneMode); + }); + return InteractionResult.CONSUME; + } else { + return InteractionResult.SUCCESS; + } + } + + public boolean stillValid(Player player) { + if (this.level.getBlockEntity(this.worldPosition) != this) { + return false; + } else { + return player.distanceToSqr(this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 0.5, this.worldPosition.getZ() + 0.5) <= 64.0; + } + } + protected abstract boolean isRunning(); + + protected abstract void tryStartRunning(); + + // Only called serverside + protected abstract void runMachineTick(); + + protected abstract int getEnergyConsumption(); + + protected void noEnergyTick() {} + + public static class ServerTicker> implements BlockEntityTicker { + @Override + public void tick(Level level, BlockPos pos, BlockState state, M machine) { + if (machine.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_IGNORED || ((machine.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_UNPOWERED)) != machine.hasRedstonePower) { + var energyConsumption = machine.getEnergyConsumption(); + + if (machine.energy.getEnergyStored() >= energyConsumption) { + if (!machine.isRunning()) { + machine.tryStartRunning(); + } + if (machine.isRunning()) { + machine.energy.extractEnergy(energyConsumption, false); + machine.runMachineTick(); + } + } else { + machine.noEnergyTick(); + } + } + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java index a418163b..eb4bc286 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java @@ -63,19 +63,9 @@ public abstract class AbstractSieveBlockEntity extends EBlockEntity implements S this.logic.loadNbt(nbt); } - // Used for rendering and for TOP - public ItemStack getMesh() { - return this.logic.getMesh(); - } - - // Used for rendering - public float getProgress() { - return this.logic.getProgress(); - } - - // Used for rendering - public ItemStack getContents() { - return this.logic.getContents(); + @Override + public SieveLogic getLogic() { + return this.logic; } @Override diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerBlockEntity.java new file mode 100644 index 00000000..19a8527c --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerBlockEntity.java @@ -0,0 +1,263 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.block.MechanicalHammerBlock; +import thedarkcolour.exdeorum.blockentity.helper.ItemHelper; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; +import thedarkcolour.exdeorum.loot.HammerLootModifier; +import thedarkcolour.exdeorum.recipe.RecipeUtil; +import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; +import thedarkcolour.exdeorum.registry.EBlockEntities; +import thedarkcolour.exdeorum.tag.EItemTags; + +public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity { + private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_HAMMER_SCREEN_TITLE); + private static final int INPUT_SLOT = 0; + public static final int HAMMER_SLOT = 1; + private static final int OUTPUT_SLOT = 2; + public static final int TOTAL_PROGRESS = 10_000_000; + // process should take 200 ticks or 10 seconds with no efficiency + private static final int PROGRESS_INTERVAL = TOTAL_PROGRESS / 200; + public static final int NOT_RUNNING = -1; + + // an integer from 0 to 10,000,000 instead of a decimal number which is inaccurate and buggy + private int progress = NOT_RUNNING; + private float efficiency; + + public MechanicalHammerBlockEntity(BlockPos pos, BlockState state) { + super(EBlockEntities.MECHANICAL_HAMMER.get(), pos, state, ItemHandler::new, EConfig.SERVER.mechanicalHammerEnergyStorage.get()); + } + + public static boolean isValidInput(ItemStack stack) { + return RecipeUtil.getHammerRecipe(stack.getItem()) != null; + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + + nbt.putInt("progress", this.progress); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + + this.progress = nbt.getInt("progress"); + onHammerChanged(); + } + + @Override + public Component getDisplayName() { + return TITLE; + } + + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player pPlayer) { + return new MechanicalHammerMenu(containerId, playerInventory, this); + } + + @Override + protected boolean isRunning() { + return this.progress != NOT_RUNNING; + } + + @Override + protected void tryStartRunning() { + var input = this.inventory.getStackInSlot(INPUT_SLOT); + + if (!input.isEmpty()) { + if (canFitResultIntoOutput(input) != null) { + this.progress = 0; + this.level.setBlock(this.worldPosition, this.getBlockState().setValue(MechanicalHammerBlock.RUNNING, true), 3); + + return; + } + } + + this.level.setBlock(this.worldPosition, this.getBlockState().setValue(MechanicalHammerBlock.RUNNING, false), 3); + } + + @Override + protected void noEnergyTick() { + if (getBlockState().getValue(MechanicalHammerBlock.RUNNING)) { + this.level.setBlock(this.worldPosition, this.getBlockState().setValue(MechanicalHammerBlock.RUNNING, false), 3); + } + } + + @Nullable + private HammerRecipe canFitResultIntoOutput(ItemStack input) { + var output = this.inventory.getStackInSlot(OUTPUT_SLOT); + + if (output.isEmpty() || output.getCount() < output.getMaxStackSize()) { + var recipe = RecipeUtil.getHammerRecipe(input.getItem()); + + if (recipe != null && (output.isEmpty() || matchesStack(recipe.result, output))) { + return recipe; + } + } + + return null; + } + + private static boolean matchesStack(Item item, ItemStack stack) { + return !stack.hasTag() && item == stack.getItem(); + } + + @Override + protected void runMachineTick() { + var input = this.inventory.getStackInSlot(INPUT_SLOT); + + if (!input.isEmpty()) { + this.progress += PROGRESS_INTERVAL * this.efficiency; + + if (this.progress >= TOTAL_PROGRESS) { + var recipe = canFitResultIntoOutput(input); + + if (recipe != null) { + @SuppressWarnings("DataFlowIssue") + LootContext ctx = RecipeUtil.emptyLootContext((ServerLevel) this.level); + var resultCount = recipe.resultAmount.getInt(ctx); + resultCount += HammerLootModifier.calculateFortuneBonus(this.inventory.getStackInSlot(HAMMER_SLOT), ctx.getRandom(), resultCount == 0); + var output = this.inventory.getStackInSlot(OUTPUT_SLOT); + if (output.isEmpty()) { + this.inventory.setStackInSlot(OUTPUT_SLOT, new ItemStack(recipe.result, resultCount)); + } else { + output.setCount(Math.min(output.getMaxStackSize(), resultCount + output.getCount())); + } + input.shrink(1); + damageHammer(ctx.getRandom()); + + setChanged(); + } + + this.progress = NOT_RUNNING; + } + } else { + this.level.setBlock(this.worldPosition, this.getBlockState().setValue(MechanicalHammerBlock.RUNNING, false), 3); + } + } + + private void damageHammer(RandomSource rand) { + var hammer = this.inventory.getStackInSlot(HAMMER_SLOT); + + if (hammer.isDamageableItem()) { + + if (hammer.hurt(1, rand, null)) { + hammer.shrink(1); + + if (hammer.isEmpty()) { + this.inventory.setStackInSlot(HAMMER_SLOT, ItemStack.EMPTY); + } + } + } + } + + private void onHammerChanged() { + var hammer = this.inventory.getStackInSlot(HAMMER_SLOT); + if (hammer.isEmpty()) { + this.efficiency = 1f; + } else { + // This timing allows full efficiency hammer to match full efficiency sieve (55 ticks/craft + // Rewards player for using hammer by doubling speed right off the bat, before efficiency + // although not as fast as Mekanism's crusher, still pretty fast and much cheaper + this.efficiency = 2f + hammer.getEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY) * 0.33f; + } + } + + @Override + protected int getEnergyConsumption() { + return EConfig.SERVER.mechanicalHammerEnergyConsumption.get(); + } + + // The value synced to the client for rendering the arrow in GUI + public int getGuiProgress() { + return Math.round((float)(24 * this.progress) / TOTAL_PROGRESS); + } + + public void setGuiProgress(int guiProgress) { + this.progress = (guiProgress * TOTAL_PROGRESS) / 24; + } + + public int getProgress() { + return this.progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + private static class ItemHandler extends ItemHelper { + private final MechanicalHammerBlockEntity hammer; + + public ItemHandler(MechanicalHammerBlockEntity hammer) { + super(3); + this.hammer = hammer; + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + if (slot == INPUT_SLOT) { + return RecipeUtil.getHammerRecipe(stack.getItem()) != null; + } else if (slot == HAMMER_SLOT) { + return stack.is(EItemTags.HAMMERS); + } else { + return false; + } + } + + @Override + public int getSlotLimit(int slot) { + return slot == HAMMER_SLOT ? 1 : super.getSlotLimit(slot); + } + + @Override + public boolean canMachineExtract(int slot) { + return slot == OUTPUT_SLOT; + } + + @Override + protected void onContentsChanged(int slot) { + if (slot == HAMMER_SLOT) { + this.hammer.onHammerChanged(); + } else if (slot == INPUT_SLOT) { + if (getStackInSlot(INPUT_SLOT).isEmpty()) { + this.hammer.progress = NOT_RUNNING; + } + } + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerMenu.java b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerMenu.java new file mode 100644 index 00000000..c7dfa2c2 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalHammerMenu.java @@ -0,0 +1,116 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.ItemStack; +import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.menu.AbstractMachineMenu; +import thedarkcolour.exdeorum.registry.EMenus; +import thedarkcolour.exdeorum.tag.EItemTags; + +public class MechanicalHammerMenu extends AbstractMachineMenu { + private static final ResourceLocation EMPTY_SLOT_HAMMER = new ResourceLocation(ExDeorum.ID, "item/empty_slot_hammer"); + private static final int NUM_SLOTS = 3; + + public MechanicalHammerMenu(int containerId, Inventory playerInventory, FriendlyByteBuf data) { + this(containerId, playerInventory, (MechanicalHammerBlockEntity) readPayload(playerInventory, data)); + } + + public MechanicalHammerMenu(int containerId, Inventory playerInventory, MechanicalHammerBlockEntity machine) { + super(EMenus.MECHANICAL_HAMMER.get(), containerId, playerInventory, machine); + + // input slot + addSlot(machine.inventory.createSlot(0, 32, 35)); + // hammer slot + addSlot(machine.inventory.createSlot(1, 56, 35).setBackground(InventoryMenu.BLOCK_ATLAS, EMPTY_SLOT_HAMMER)); + // output slot + addSlot(machine.inventory.createSlot(2, 116, 35)); + + addPlayerSlots(playerInventory, 84); + + addDataSlot(new ProgressDataSlot()); + } + + @Override + public ItemStack quickMoveStack(Player player, int clickedSlot) { + var stack = ItemStack.EMPTY; + var slot = this.slots.get(clickedSlot); + + if (slot.hasItem()) { + var clickedStack = slot.getItem(); + stack = clickedStack.copy(); + + if (clickedSlot > 1 && clickedSlot <= NUM_SLOTS) { // moving out of output slots + if (!moveItemStackTo(clickedStack, NUM_SLOTS, PLAYER_SLOTS + NUM_SLOTS, true)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < 2) { // moving out of input/mesh slot + if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) { + return ItemStack.EMPTY; + } + } else if (MechanicalHammerBlockEntity.isValidInput(clickedStack)) { // attempting to move into input slot + if (!moveItemStackTo(clickedStack, 0, 1, false)) { + return ItemStack.EMPTY; + } + } else if (clickedStack.is(EItemTags.HAMMERS)) { // attempting to move into mesh slot + if (!moveItemStackTo(clickedStack, 1, 2, false)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < NUM_SLOTS + 27) { // attempting to move from inventory to hotbar + if (!moveItemStackTo(clickedStack, NUM_SLOTS + 27, NUM_SLOTS + PLAYER_SLOTS, false)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < NUM_SLOTS + PLAYER_SLOTS) { // attempting to move from hotbar to inventory + if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + 27, false)) { + return ItemStack.EMPTY; + } + } + + if (clickedStack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } + slot.setChanged(); + if (clickedStack.getCount() == stack.getCount()) { + return ItemStack.EMPTY; + } + slot.onTake(player, clickedStack); + broadcastChanges(); + } + + return stack; + } + + private class ProgressDataSlot extends DataSlot { + @Override + public int get() { + return MechanicalHammerMenu.this.machine.getGuiProgress(); + } + + @Override + public void set(int value) { + MechanicalHammerMenu.this.machine.setGuiProgress(value); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java index cfec52a1..37de00f4 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java @@ -19,35 +19,19 @@ package thedarkcolour.exdeorum.blockentity; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.energy.EnergyStorage; import net.minecraftforge.items.ItemHandlerHelper; -import net.minecraftforge.network.NetworkHooks; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import thedarkcolour.exdeorum.blockentity.helper.EnergyHelper; import thedarkcolour.exdeorum.blockentity.helper.ItemHelper; import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; -import thedarkcolour.exdeorum.client.screen.RedstoneControlWidget; import thedarkcolour.exdeorum.config.EConfig; import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; @@ -55,107 +39,70 @@ import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.registry.EBlockEntities; import thedarkcolour.exdeorum.tag.EItemTags; -import javax.annotation.Nonnull; - -public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity implements MenuProvider { +public class MechanicalSieveBlockEntity extends AbstractMachineBlockEntity implements SieveLogic.Owner { private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE); private static final int INPUT_SLOT = 0; public static final int MESH_SLOT = 1; - public final ItemHelper inventory; - public final EnergyHelper energy; - private int redstoneMode; - // not saved to NBT - public boolean hasRedstonePower; - - private final LazyOptional capabilityInventory; - private final LazyOptional capabilityEnergy; + private final SieveLogic logic; public MechanicalSieveBlockEntity(BlockPos pos, BlockState state) { - super(EBlockEntities.MECHANICAL_SIEVE.get(), pos, state, owner -> new SieveLogic(owner, false, true)); + super(EBlockEntities.MECHANICAL_SIEVE.get(), pos, state, ItemHandler::new, EConfig.SERVER.mechanicalSieveEnergyStorage.get()); - this.inventory = new ItemHandler(22); - this.energy = new EnergyHelper(40000); - - this.capabilityInventory = LazyOptional.of(() -> this.inventory); - this.capabilityEnergy = LazyOptional.of(() -> this.energy); + this.logic = new SieveLogic(this, false, true); } @Override protected void saveAdditional(CompoundTag nbt) { super.saveAdditional(nbt); - nbt.put("inventory", this.inventory.serializeNBT()); - nbt.putInt("energy", this.energy.getEnergyStored()); - nbt.putInt("redstoneMode", this.redstoneMode); + this.logic.saveNbt(nbt); } @Override public void load(CompoundTag nbt) { super.load(nbt); - this.inventory.deserializeNBT(nbt.getCompound("inventory")); - this.energy.setStoredEnergy(nbt.getInt("energy")); - this.redstoneMode = Mth.clamp(nbt.getInt("redstoneMode"), 0, 2); + this.logic.loadNbt(nbt); } @Override - public void onLoad() { - checkPoweredState(this.level, this.worldPosition); + protected boolean isRunning() { + return !this.logic.getContents().isEmpty(); } @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - if (player instanceof ServerPlayer serverPlayer) { - NetworkHooks.openScreen(serverPlayer, this, buffer -> { - buffer.writeBlockPos(getBlockPos()); - buffer.writeByte(this.redstoneMode); - }); - return InteractionResult.CONSUME; - } else { - return InteractionResult.SUCCESS; + protected void tryStartRunning() { + var input = this.inventory.getStackInSlot(INPUT_SLOT); + + if (this.logic.isValidInput(input)) { + this.logic.startSifting(AbstractSieveBlockEntity.singleCopy(input)); + input.shrink(1); } } - @Nonnull @Override - public LazyOptional getCapability(@Nonnull Capability cap, @javax.annotation.Nullable Direction side) { - if (cap == ForgeCapabilities.ENERGY) { - return this.capabilityEnergy.cast(); - } else if (cap == ForgeCapabilities.ITEM_HANDLER) { - return this.capabilityInventory.cast(); - } - - return super.getCapability(cap, side); + protected void runMachineTick() { + this.logic.sift(0.01f); } @Override - public void invalidateCaps() { - super.invalidateCaps(); - - this.capabilityEnergy.invalidate(); - this.capabilityInventory.invalidate(); + protected int getEnergyConsumption() { + return EConfig.SERVER.mechanicalSieveEnergyConsumption.get(); } - private void serverTick() { - if (this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_IGNORED || ((this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_UNPOWERED)) != this.hasRedstonePower) { - var energyConsumption = EConfig.SERVER.mechanicalSieveEnergyConsumption.get(); + @Override + public void writeVisualData(FriendlyByteBuf buffer) { + buffer.writeItem(this.logic.getMesh()); + buffer.writeFloat(this.logic.getProgress()); + buffer.writeItem(this.logic.getContents()); + } - if (this.energy.getEnergyStored() >= energyConsumption) { - if (this.logic.getContents().isEmpty()) { - var input = this.inventory.getStackInSlot(INPUT_SLOT); - - if (this.logic.isValidInput(input)) { - this.logic.startSifting(AbstractSieveBlockEntity.singleCopy(input)); - input.shrink(1); - } - } - if (!this.logic.getContents().isEmpty()) { - this.energy.extractEnergy(energyConsumption, false); - this.logic.sift(0.01f); - } - } - } + @Override + public void readVisualData(FriendlyByteBuf buffer) { + this.logic.setMesh(buffer.readItem(), false); + this.logic.setProgress(buffer.readFloat()); + this.logic.setContents(buffer.readItem()); } @Override @@ -205,43 +152,32 @@ public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity impleme return TITLE; } - @Nullable @Override public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player pPlayer) { return new MechanicalSieveMenu(containerId, playerInventory, this); } - public boolean stillValid(Player player) { - if (this.level.getBlockEntity(this.worldPosition) != this) { - return false; - } else { - return player.distanceToSqr(this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 0.5, this.worldPosition.getZ() + 0.5) <= 64.0; - } - } - + @Override public SieveLogic getLogic() { return this.logic; } - public void setRedstoneMode(int redstoneMode) { - this.redstoneMode = redstoneMode; + @SuppressWarnings("DataFlowIssue") + @Override + public ServerLevel getServerLevel() { + return (ServerLevel) this.level; } - public int getRedstoneMode() { - return this.redstoneMode; - } + private static class ItemHandler extends ItemHelper { + private final MechanicalSieveBlockEntity sieve; - public void checkPoweredState(Level level, BlockPos pos) { - this.hasRedstonePower = level.hasNeighborSignal(pos); - } - - private class ItemHandler extends ItemHelper { - public ItemHandler(int size) { - super(size); + public ItemHandler(MechanicalSieveBlockEntity sieve) { + super(22); + this.sieve = sieve; } @Override - public boolean isItemValid(int slot, @NotNull ItemStack stack) { + public boolean isItemValid(int slot, ItemStack stack) { if (slot == INPUT_SLOT) { return !RecipeUtil.getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty(); } else if (slot == MESH_SLOT) { @@ -264,20 +200,13 @@ public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity impleme @Override protected void onContentsChanged(int slot) { if (slot == MESH_SLOT) { - MechanicalSieveBlockEntity.this.logic.setMesh(MechanicalSieveBlockEntity.this.inventory.getStackInSlot(MESH_SLOT)); + this.sieve.logic.setMesh(this.sieve.inventory.getStackInSlot(MESH_SLOT)); } } @Override protected void onLoad() { - MechanicalSieveBlockEntity.this.logic.setMesh(MechanicalSieveBlockEntity.this.inventory.getStackInSlot(MESH_SLOT), false); - } - } - - public static class ServerTicker implements BlockEntityTicker { - @Override - public void tick(Level level, BlockPos pos, BlockState state, MechanicalSieveBlockEntity sieve) { - sieve.serverTick(); + this.sieve.logic.setMesh(this.sieve.inventory.getStackInSlot(MESH_SLOT), false); } } } diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java b/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java index f2a6780a..6eed7556 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java @@ -27,14 +27,11 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.LootParams; import thedarkcolour.exdeorum.config.EConfig; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; import thedarkcolour.exdeorum.tag.EItemTags; -import java.util.Map; - public class SieveLogic { private final Owner owner; private final boolean saveMesh; @@ -80,7 +77,7 @@ public class SieveLogic { // Need epsilon because floating point decimals suck if (this.progress >= 1.0f - Mth.EPSILON) { var level = this.owner.getServerLevel(); - var context = new LootContext.Builder(new LootParams(level, Map.of(), Map.of(), 0)).create(null); + var context = RecipeUtil.emptyLootContext(level); var rand = level.random; var limitDrops = this.contents.getItem() == Items.MOSS_BLOCK && EConfig.SERVER.limitMossSieveDrops.get(); var handledAnyDrops = false; @@ -207,5 +204,7 @@ public class SieveLogic { boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand); void markUpdated(); + + SieveLogic getLogic(); } } diff --git a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java index 1e9a3062..6581ab5e 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java @@ -43,6 +43,7 @@ import net.minecraftforge.fml.event.config.ModConfigEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen; import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.client.ter.BarrelRenderer; import thedarkcolour.exdeorum.client.ter.CrucibleRenderer; @@ -101,6 +102,7 @@ public class ClientHandler { event.enqueueWork(() -> { setRenderLayers(); MenuScreens.register(EMenus.MECHANICAL_SIEVE.get(), MechanicalSieveScreen::new); + MenuScreens.register(EMenus.MECHANICAL_HAMMER.get(), MechanicalHammerScreen::new); }); } diff --git a/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalHammerScreen.java b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalHammerScreen.java new file mode 100644 index 00000000..e5b81434 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalHammerScreen.java @@ -0,0 +1,94 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.client.screen; + +import net.minecraft.client.Minecraft; +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.util.Mth; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.blockentity.MechanicalHammerMenu; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; + +public class MechanicalHammerScreen extends AbstractContainerScreen { + private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExDeorum.ID, "textures/gui/container/mechanical_hammer.png"); + + public static final int RECIPE_CLICK_AREA_POS_X = 80; + public static final int RECIPE_CLICK_AREA_POS_Y = 34; + public static final int RECIPE_CLICK_AREA_WIDTH = 23; + public static final int RECIPE_CLICK_AREA_HEIGHT = 16; + + @Nullable + private RedstoneControlWidget redstoneControlWidget; + + public MechanicalHammerScreen(MechanicalHammerMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + + this.imageWidth = 176; + this.imageHeight = 166; + } + + @Override + protected void init() { + super.init(); + + this.redstoneControlWidget = new RedstoneControlWidget(this.menu, BACKGROUND_TEXTURE, this.leftPos + this.imageWidth, this.topPos + 3); + addRenderableWidget(this.redstoneControlWidget); + } + + @Nullable + public RedstoneControlWidget getRedstoneControlWidget() { + return this.redstoneControlWidget; + } + + @Override + protected void renderBg(GuiGraphics graphics, float pPartialTick, int pMouseX, int pMouseY) { + int left = this.leftPos; + int top = this.topPos; + graphics.blit(BACKGROUND_TEXTURE, left, top, 0, 0, this.imageWidth, this.imageHeight); + + // energy bar + int energy = Mth.floor(54 * ((float) this.menu.prevEnergy / EConfig.SERVER.mechanicalSieveEnergyStorage.get())); + graphics.blit(BACKGROUND_TEXTURE, left + 10, top + 15 + 54 - energy, this.imageWidth, 16 + 54 - energy, 12, energy); + + // progress arrow + int progress = Math.min(23, this.menu.machine.getGuiProgress()); + graphics.blit(BACKGROUND_TEXTURE, left + RECIPE_CLICK_AREA_POS_X, top + RECIPE_CLICK_AREA_POS_Y, this.imageWidth, 0, progress, 16); + } + + @Override + public void render(GuiGraphics graphics, int mx, int my, float pPartialTick) { + renderBackground(graphics); + super.render(graphics, mx, my, pPartialTick); + renderTooltip(graphics, mx, my); + + int rx = mx - this.leftPos; + int ry = my - this.topPos; + + if (9 <= rx && rx < 23 && 14 <= ry && ry < 70) { + var energyTooltip = Component.translatable(TranslationKeys.ENERGY).append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, this.menu.prevEnergy, EConfig.SERVER.mechanicalSieveEnergyStorage.get())).append(" FE"); + graphics.renderTooltip(Minecraft.getInstance().font, energyTooltip, mx, my); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java index d9a83842..cf084c5a 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java +++ b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java @@ -40,14 +40,12 @@ public class MechanicalSieveScreen extends AbstractContainerScreen screen; private final ResourceLocation texture; private final int posX; private final int posY; @@ -68,7 +69,7 @@ public class RedstoneControlWidget implements GuiEventListener, NarratableEntry, // Last time (from currentTimeMillis) this button was clicked, used in animation lerp private long lastClicked = -1L; - public RedstoneControlWidget(MechanicalSieveScreen screen, ResourceLocation texture, int posX, int posY) { + public RedstoneControlWidget(AbstractMachineMenu screen, ResourceLocation texture, int posX, int posY) { this.screen = screen; this.texture = texture; this.posX = posX; @@ -106,7 +107,7 @@ public class RedstoneControlWidget implements GuiEventListener, NarratableEntry, var font = Minecraft.getInstance().font; if (this.expanded) { - var redstoneMode = this.screen.getMenu().sieve.getRedstoneMode(); + var redstoneMode = this.screen.machine.getRedstoneMode(); graphics.blit(this.texture, this.posX, this.posY, this.expandedU, this.expandedV, this.expandedWidth, this.expandedHeight); for (int i = 0; i < 3; ++i) { graphics.blit(this.texture, this.buttonsPosX + (i * 19), this.buttonsPosY, (redstoneMode == i ? this.tabU + 16 : this.tabU), this.tabV + this.tabHeight, 16, 16); @@ -157,9 +158,10 @@ public class RedstoneControlWidget implements GuiEventListener, NarratableEntry, return false; } + @SuppressWarnings("DataFlowIssue") private void setRedstoneMode(int redstoneMode) { - this.screen.getMenu().clickMenuButton(Minecraft.getInstance().player, redstoneMode); - Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.screen.getMenu().containerId, redstoneMode); + this.screen.clickMenuButton(Minecraft.getInstance().player, redstoneMode); + Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.screen.containerId, redstoneMode); } private void drawPartialConfig(GuiGraphics graphics) { diff --git a/src/main/java/thedarkcolour/exdeorum/client/ter/SieveRenderer.java b/src/main/java/thedarkcolour/exdeorum/client/ter/SieveRenderer.java index 8812ae51..e21c909f 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/ter/SieveRenderer.java +++ b/src/main/java/thedarkcolour/exdeorum/client/ter/SieveRenderer.java @@ -23,31 +23,33 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; -import net.minecraftforge.registries.ForgeRegistries; -import thedarkcolour.exdeorum.blockentity.AbstractSieveBlockEntity; +import thedarkcolour.exdeorum.blockentity.EBlockEntity; +import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; import thedarkcolour.exdeorum.client.RenderUtil; import java.util.HashMap; import java.util.Map; -public class SieveRenderer implements BlockEntityRenderer { +public class SieveRenderer implements BlockEntityRenderer { public static final Map MESH_TEXTURES = new HashMap<>(); @Override public void render(T sieve, float partialTicks, PoseStack stack, MultiBufferSource buffers, int light, int overlay) { - var contents = sieve.getContents(); + var logic = sieve.getLogic(); + var contents = logic.getContents(); if (!contents.isEmpty() && contents.getItem() instanceof BlockItem blockItem) { var block = blockItem.getBlock(); - var percentage = sieve.getProgress(); + var percentage = logic.getProgress(); var face = RenderUtil.getTopFace(block); face.renderFlatSpriteLerp(buffers, stack, percentage, 0xff, 0xff, 0xff, light, 1.0f, 15f, 13f); } - var mesh = sieve.getMesh(); + var mesh = logic.getMesh(); if (!mesh.isEmpty()) { var builder = buffers.getBuffer(RenderType.cutoutMipped()); @@ -57,7 +59,8 @@ public class SieveRenderer implements BlockE if (MESH_TEXTURES.containsKey(meshItem)) { meshSprite = MESH_TEXTURES.get(meshItem); } else { - ResourceLocation registryName = ForgeRegistries.ITEMS.getKey(meshItem); + @SuppressWarnings("deprecation") + ResourceLocation registryName = BuiltInRegistries.ITEM.getKey(meshItem); ResourceLocation textureLoc = registryName.withPrefix("item/mesh/"); meshSprite = RenderUtil.blockAtlas.getSprite(textureLoc); MESH_TEXTURES.put(meshItem, meshSprite); diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java index 604150a1..708fc8ac 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java @@ -48,6 +48,7 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.ModList; import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; +import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen; import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.compat.GroupedSieveRecipe; import thedarkcolour.exdeorum.compat.ModIds; @@ -214,6 +215,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { registration.addRecipeCatalyst(new ItemStack(EItems.IRON_HAMMER.get()), HAMMER); registration.addRecipeCatalyst(new ItemStack(EItems.DIAMOND_HAMMER.get()), HAMMER); registration.addRecipeCatalyst(new ItemStack(EItems.NETHERITE_HAMMER.get()), HAMMER); + registration.addRecipeCatalyst(new ItemStack(EItems.MECHANICAL_HAMMER.get()), HAMMER); } @Override @@ -231,6 +233,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { registration.addItemStackInfo(new ItemStack(EItems.CRIMSON_NYLIUM_SPORES.get()), Component.translatable(TranslationKeys.CRIMSON_NYLIUM_SPORES_JEI_INFO)); registration.addItemStackInfo(new ItemStack(EItems.SCULK_CORE.get()), Component.translatable(TranslationKeys.SCULK_CORE_JEI_INFO)); registration.addItemStackInfo(new ItemStack(EItems.MECHANICAL_SIEVE.get()), Component.translatable(TranslationKeys.MECHANICAL_SIEVE_JEI_INFO)); + registration.addItemStackInfo(new ItemStack(EItems.MECHANICAL_HAMMER.get()), Component.translatable(TranslationKeys.MECHANICAL_HAMMER_JEI_INFO)); var toRemove = new ArrayList(); @@ -322,6 +325,22 @@ public class ExDeorumJeiPlugin implements IModPlugin { return List.of(); } }); + registration.addGuiContainerHandler(MechanicalHammerScreen.class, new IGuiContainerHandler<>() { + @Override + public Collection getGuiClickableAreas(MechanicalHammerScreen containerScreen, double mouseX, double mouseY) { + IGuiClickableArea clickableArea = IGuiClickableArea.createBasic(MechanicalHammerScreen.RECIPE_CLICK_AREA_POS_X, MechanicalHammerScreen.RECIPE_CLICK_AREA_POS_Y, MechanicalHammerScreen.RECIPE_CLICK_AREA_WIDTH, MechanicalHammerScreen.RECIPE_CLICK_AREA_HEIGHT, HAMMER); + return List.of(clickableArea); + } + + @Override + public List getGuiExtraAreas(MechanicalHammerScreen containerScreen) { + var widget = containerScreen.getRedstoneControlWidget(); + if (widget != null) { + return widget.getJeiBounds(); + } + return List.of(); + } + }); } private static > void addRecipes(IRecipeRegistration registration, RecipeType category, Supplier> type) { diff --git a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java index 679e144f..85bce11f 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java @@ -80,14 +80,15 @@ public class ExDeorumInfoProvider implements IProbeInfoProvider { info.tank(crucible.getTank()); } } else if (te instanceof SieveBlockEntity sieve) { - if (!sieve.getContents().isEmpty()) { - info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text((Math.round(1000 * sieve.getProgress()) / 10) + "%")); + var logic = sieve.getLogic(); + if (!logic.getContents().isEmpty()) { + info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text((Math.round(1000 * logic.getProgress()) / 10) + "%")); } if (playerEntity.isShiftKeyDown()) { - var mesh = sieve.getMesh(); + var mesh = logic.getMesh(); info.horizontal(info.defaultLayoutStyle().spacing(10).alignment(ElementAlignment.ALIGN_CENTER)) - .item(sieve.getMesh(), info.defaultItemStyle().width(16).height(16)) - .text(CompoundText.create().info(sieve.getMesh().getDescriptionId())); + .item(mesh, info.defaultItemStyle().width(16).height(16)) + .text(CompoundText.create().info(mesh.getDescriptionId())); if (mesh.isEnchanted()) { var list = new ObjectArrayList(); var style = info.defaultTextStyle().height(10); diff --git a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java index 86167f7f..95298e93 100644 --- a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java +++ b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java @@ -133,6 +133,8 @@ public class EConfig { public final BooleanValue allowWitchWaterEntityConversion; public final IntValue mechanicalSieveEnergyStorage; public final IntValue mechanicalSieveEnergyConsumption; + public final IntValue mechanicalHammerEnergyStorage; + public final IntValue mechanicalHammerEnergyConsumption; public Server(ForgeConfigSpec.Builder builder) { builder.comment("Server configuration for Ex Deorum").push("server"); @@ -179,6 +181,12 @@ public class EConfig { this.mechanicalSieveEnergyConsumption = builder .comment("The amount of FE/t a tick consumed by the mechanical sieve when sifting a block.") .defineInRange("mechanical_sieve_energy_consumption", 40, 0, Integer.MAX_VALUE); + this.mechanicalHammerEnergyStorage = builder + .comment("The maximum amount of FE the mechanical hammer can have in its energy storage.") + .defineInRange("mechanical_hammer_energy_storage", 40_000, 0, Integer.MAX_VALUE); + this.mechanicalHammerEnergyConsumption = builder + .comment("The amount of FE/t a tick consumed by the mechanical hammer when crushing a block.") + .defineInRange("mechanical_hammer_energy_consumption", 20, 0, Integer.MAX_VALUE); builder.pop(); } diff --git a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java index 7b3487a0..f154bf65 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java +++ b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java @@ -65,6 +65,11 @@ class BlockLoot extends BlockLootSubProvider { .setRolls(ConstantValue.exactly(1.0F)) .add(LootItem.lootTableItem(EItems.MECHANICAL_SIEVE.get()) .apply(MachineLootFunction.machineLoot()))))); + add(EBlocks.MECHANICAL_HAMMER.get(), LootTable.lootTable() + .withPool(applyExplosionCondition(EItems.MECHANICAL_HAMMER.get(), LootPool.lootPool() + .setRolls(ConstantValue.exactly(1.0F)) + .add(LootItem.lootTableItem(EItems.MECHANICAL_HAMMER.get()) + .apply(MachineLootFunction.machineLoot()))))); } @Override diff --git a/src/main/java/thedarkcolour/exdeorum/data/English.java b/src/main/java/thedarkcolour/exdeorum/data/English.java index c004334d..d168f930 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/English.java +++ b/src/main/java/thedarkcolour/exdeorum/data/English.java @@ -31,6 +31,7 @@ class English { english.add(TranslationKeys.VOID_WORLD_TYPE, "Void World"); english.add(TranslationKeys.FRACTION_DISPLAY, ": %s / %s"); english.add(TranslationKeys.MECHANICAL_SIEVE_MESH_LABEL, "Mesh: "); + english.add(TranslationKeys.MECHANICAL_HAMMER_HAMMER_LABEL, "Hammer: "); english.add(TranslationKeys.ENERGY, "Energy"); english.add(TranslationKeys.ROOT_ADVANCEMENT_TITLE, "Don't Look Down..."); @@ -55,6 +56,7 @@ class English { english.add(TranslationKeys.CRIMSON_NYLIUM_SPORES_JEI_INFO, "Use on netherrack to turn it into a crimson nylium block."); english.add(TranslationKeys.SCULK_CORE_JEI_INFO, "Use a sculk core on a Sculk Shrieker to enable it to spawn Wardens. Normally, Sculk Shriekers placed by players cannot spawn Wardens, so this item is useful for obtaining Sculk items in a SkyBlock world."); english.add(TranslationKeys.MECHANICAL_SIEVE_JEI_INFO, "The Mechanical Sieve is a machine that, when supplied with a mesh and Forge Energy (FE), will sift blocks without a player having to do it themselves. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power."); + english.add(TranslationKeys.MECHANICAL_HAMMER_JEI_INFO, "The Mechanical Hammer is a machine that, when supplied with Forge Energy (FE), will hammer blocks without a player having to do it themselves. It can operate without a hammer, but adding any hammer will double the speed, and efficiency enchantments on the hammer will further increase speed. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power."); english.add(TranslationKeys.BARREL_COMPOST_CATEGORY_TITLE, "Barrel Compost"); english.add(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, "Compost: %s"); @@ -79,6 +81,7 @@ class English { english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_POWERED], "Powered"); english.add(TranslationKeys.REDSTONE_CONTROL_LABEL, "Redstone Mode"); english.add(TranslationKeys.REDSTONE_CONTROL_MODE, "Mode: "); + english.add(TranslationKeys.MECHANICAL_HAMMER_SCREEN_TITLE, "Mechanical Hammer"); english.addBlock(EBlocks.VEXING_ARCHWOOD_CRUCIBLE, "Vexing Archwood Crucible"); english.addBlock(EBlocks.CASCADING_ARCHWOOD_CRUCIBLE, "Cascading Archwood Crucible"); diff --git a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java index 0771e36b..f53c9e81 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java +++ b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java @@ -27,6 +27,7 @@ public class TranslationKeys { // ": %s / %s" public static final String FRACTION_DISPLAY = "item." + ExDeorum.ID + ".watering_can_fluid_display"; public static final String MECHANICAL_SIEVE_MESH_LABEL = "item." + ExDeorum.ID + ".mechanical_sieve.mesh_label"; + public static final String MECHANICAL_HAMMER_HAMMER_LABEL = "item." + ExDeorum.ID + ".mechanical_hammer.hammer_label"; public static final String ENERGY = "gui." + ExDeorum.ID + ".energy_label"; // Advancements @@ -53,6 +54,7 @@ public class TranslationKeys { public static final String CRIMSON_NYLIUM_SPORES_JEI_INFO = "info." + ExDeorum.ID + ".crimson_nylium_spores"; public static final String SCULK_CORE_JEI_INFO = "info." + ExDeorum.ID + ".sculk_core"; public static final String MECHANICAL_SIEVE_JEI_INFO = "info." + ExDeorum.ID + ".mechanical_sieve"; + public static final String MECHANICAL_HAMMER_JEI_INFO = "info." + ExDeorum.ID + ".mechanical_hammer"; // JEI recipe categories public static final String BARREL_COMPOST_CATEGORY_TITLE = "gui." + ExDeorum.ID + ".category.barrel_compost"; @@ -81,4 +83,5 @@ public class TranslationKeys { }; public static final String REDSTONE_CONTROL_LABEL = "gui." + ExDeorum.ID + ".redstone_control.label"; public static final String REDSTONE_CONTROL_MODE = "gui." + ExDeorum.ID + ".redstone_control.mode"; + public static final String MECHANICAL_HAMMER_SCREEN_TITLE = ExDeorum.ID + ".container.mechanical_hammer"; } diff --git a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java index 7593fd6a..43937616 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java +++ b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java @@ -293,6 +293,16 @@ public class Recipes { recipe.pattern("I I"); MKRecipeProvider.unlockedByHaving(recipe, Items.HOPPER); }); + recipes.shapedCrafting(RecipeCategory.MISC, EItems.MECHANICAL_HAMMER.get(), recipe -> { + recipe.define('#', Items.IRON_BLOCK); + recipe.define('H', Items.HOPPER); + recipe.define('T', EItemTags.HAMMERS); + recipe.define('I', Items.IRON_INGOT); + recipe.pattern("III"); + recipe.pattern("ITI"); + recipe.pattern("#H#"); + MKRecipeProvider.unlockedByHaving(recipe, Items.HOPPER); + }); } private static void modUShaped(MKRecipeProvider recipes, String modid, RegistryObject sides, RegistryObject middle, RegistryObject result) { diff --git a/src/main/java/thedarkcolour/exdeorum/loot/HammerLootModifier.java b/src/main/java/thedarkcolour/exdeorum/loot/HammerLootModifier.java index e639fae8..2c71ca66 100644 --- a/src/main/java/thedarkcolour/exdeorum/loot/HammerLootModifier.java +++ b/src/main/java/thedarkcolour/exdeorum/loot/HammerLootModifier.java @@ -21,6 +21,7 @@ package thedarkcolour.exdeorum.loot; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantments; @@ -56,22 +57,8 @@ public class HammerLootModifier extends LootModifier { // fortune handling; more likely to boost drops if there are none to begin with if (context.hasParam(LootContextParams.TOOL)) { - var stack = context.getParam(LootContextParams.TOOL); - var fortune = stack.getEnchantmentLevel(Enchantments.BLOCK_FORTUNE); - - if (fortune != 0) { - var chance = context.getRandom().nextFloat(); - - if (resultAmount == 0) { - if (chance < 0.06 * fortune) { - resultAmount++; - } - } else { - if (chance < 0.03 * fortune) { - resultAmount++; - } - } - } + var hammer = context.getParam(LootContextParams.TOOL); + resultAmount += calculateFortuneBonus(hammer, context.getRandom(), resultAmount == 0); } if (resultAmount > 0) { @@ -89,5 +76,32 @@ public class HammerLootModifier extends LootModifier { public Codec codec() { return CODEC; } + + /** + * Calculates the bonus number of drops for a hammer enchanted with fortune. + * @param hammer The hammer in question + * @param rand RNG + * @param zeroBaseDrops Whether there were no drops to begin with + * @return The additional number of drops, to be added to the number of base drops + */ + public static int calculateFortuneBonus(ItemStack hammer, RandomSource rand, boolean zeroBaseDrops) { + var fortune = hammer.getEnchantmentLevel(Enchantments.BLOCK_FORTUNE); + + if (fortune != 0) { + var chance = rand.nextFloat(); + + if (zeroBaseDrops) { + if (chance < 0.06f * fortune) { + return 1; + } + } else { + if (chance < 0.03f * fortune) { + return 1; + } + } + } + + return 0; + } } diff --git a/src/main/java/thedarkcolour/exdeorum/menu/AbstractMachineMenu.java b/src/main/java/thedarkcolour/exdeorum/menu/AbstractMachineMenu.java new file mode 100644 index 00000000..16f7a35f --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/menu/AbstractMachineMenu.java @@ -0,0 +1,121 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.menu; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +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 org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.blockentity.AbstractMachineBlockEntity; +import thedarkcolour.exdeorum.network.NetworkHandler; + +public abstract class AbstractMachineMenu> extends AbstractContainerMenu { + protected static final int PLAYER_SLOTS = 36; // hotbar + inventory + + @Nullable + private final ServerPlayer player; + public final M machine; + + public int prevEnergy; + + protected AbstractMachineMenu(MenuType pMenuType, int pContainerId, Inventory playerInventory, M machine) { + super(pMenuType, pContainerId); + + this.machine = machine; + + if (playerInventory.player instanceof ServerPlayer serverPlayer) { + this.player = serverPlayer; + } else { + this.player = null; + } + } + + // todo find a better way to do this + @SuppressWarnings({"DataFlowIssue", "unchecked"}) + protected static > M readPayload(Inventory playerInventory, FriendlyByteBuf data) { + var machine = (M) playerInventory.player.level().getBlockEntity(data.readBlockPos()); + machine.setRedstoneMode(data.readByte()); + return machine; + } + + // Call after own slots have been added + protected final void addPlayerSlots(Inventory playerInventory, int startY) { + // Inventory + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, startY + i * 18)); + } + } + // Hotbar + for (int k = 0; k < 9; ++k) { + addSlot(new Slot(playerInventory, k, 8 + k * 18, startY + 58)); + } + } + + // When the server sends a menu property message, the client handles the synced property here. + public void setClientProperty(int index, int value) { + if (index == 0) { + this.prevEnergy = value; + } + } + + @Override + public void broadcastChanges() { + super.broadcastChanges(); + + if (this.player != null) { + syncProperties(this.player); + } + } + + @Override + public void broadcastFullState() { + super.broadcastFullState(); + + if (this.player != null) { + syncProperties(this.player); + } + } + + protected void syncProperties(ServerPlayer player) { + if (this.prevEnergy != this.machine.energy.getEnergyStored()) { + this.prevEnergy = this.machine.energy.getEnergyStored(); + + NetworkHandler.sendMenuProperty(player, this.containerId, 0, this.prevEnergy); + } + } + + @Override + public boolean clickMenuButton(Player pPlayer, int id) { + if (0 <= id && id < 3) { + this.machine.setRedstoneMode(id); + return true; + } + return false; + } + + @Override + public boolean stillValid(Player player) { + return this.machine.stillValid(player); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java b/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java deleted file mode 100644 index f298089b..00000000 --- a/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Ex Deorum - * Copyright (c) 2024 thedarkcolour - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package thedarkcolour.exdeorum.menu; - -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; - -public abstract class EContainerMenu extends AbstractContainerMenu { - protected EContainerMenu(MenuType pMenuType, int pContainerId) { - super(pMenuType, pContainerId); - } - - // When the server sends a menu property message, the client handles the synced property here. - public abstract void setClientProperty(int index, int value); -} diff --git a/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java b/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java index c78b20e4..79bee067 100644 --- a/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java +++ b/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java @@ -20,38 +20,24 @@ package thedarkcolour.exdeorum.menu; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.Nullable; import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; -import thedarkcolour.exdeorum.network.NetworkHandler; import thedarkcolour.exdeorum.registry.EMenus; -public class MechanicalSieveMenu extends EContainerMenu { +public class MechanicalSieveMenu extends AbstractMachineMenu { private static final ResourceLocation EMPTY_SLOT_MESH = new ResourceLocation(ExDeorum.ID, "item/empty_slot_mesh"); private static final int NUM_SLOTS = 22; // input + mesh, 20 output slots - private static final int PLAYER_SLOTS = 36; // hotbar + inventory - - public final MechanicalSieveBlockEntity sieve; - @Nullable - private final ServerPlayer player; - - public int prevSieveEnergy; public MechanicalSieveMenu(int containerId, Inventory playerInventory, FriendlyByteBuf data) { - this(containerId, playerInventory, (MechanicalSieveBlockEntity) playerInventory.player.level().getBlockEntity(data.readBlockPos())); - this.sieve.setRedstoneMode(data.readByte()); + this(containerId, playerInventory, (MechanicalSieveBlockEntity) readPayload(playerInventory, data)); } public MechanicalSieveMenu(int containerId, Inventory playerInventory, MechanicalSieveBlockEntity sieve) { - super(EMenus.MECHANICAL_SIEVE.get(), containerId); - - this.sieve = sieve; + super(EMenus.MECHANICAL_SIEVE.get(), containerId, playerInventory, sieve); // input slot addSlot(sieve.inventory.createSlot(0, 26, 30)); @@ -63,54 +49,7 @@ public class MechanicalSieveMenu extends EContainerMenu { addSlot(sieve.inventory.createSlot(2 + r * 5 + c, 80 + c * 18, 15 + r * 18)); } } - // Player slots - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 9; ++j) { - addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 7 + 84 + i * 18)); - } - } - for (int k = 0; k < 9; ++k) { - addSlot(new Slot(playerInventory, k, 8 + k * 18, 149)); - } - - if (playerInventory.player instanceof ServerPlayer serverPlayer) { - this.player = serverPlayer; - } else { - this.player = null; - } - } - - @Override - public void broadcastChanges() { - super.broadcastChanges(); - - if (this.player != null) { - if (this.prevSieveEnergy != this.sieve.energy.getEnergyStored()) { - this.prevSieveEnergy = this.sieve.energy.getEnergyStored(); - - NetworkHandler.sendMenuProperty(this.player, this.containerId, 0, this.prevSieveEnergy); - } - } - } - - @Override - public void broadcastFullState() { - super.broadcastFullState(); - - if (this.player != null) { - if (this.prevSieveEnergy != this.sieve.energy.getEnergyStored()) { - this.prevSieveEnergy = this.sieve.energy.getEnergyStored(); - - NetworkHandler.sendMenuProperty(this.player, this.containerId, 0, this.prevSieveEnergy); - } - } - } - - @Override - public void setClientProperty(int index, int value) { - if (index == 0) { - this.prevSieveEnergy = value; - } + addPlayerSlots(playerInventory, 91); } @Override @@ -130,11 +69,11 @@ public class MechanicalSieveMenu extends EContainerMenu { if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) { return ItemStack.EMPTY; } - } else if (this.sieve.getLogic().isValidInput(clickedStack)) { // attempting to move into input slot + } else if (this.machine.getLogic().isValidInput(clickedStack)) { // attempting to move into input slot if (!moveItemStackTo(clickedStack, 0, 1, false)) { return ItemStack.EMPTY; } - } else if (this.sieve.getLogic().isValidMesh(clickedStack)) { // attempting to move into mesh slot + } else if (this.machine.getLogic().isValidMesh(clickedStack)) { // attempting to move into mesh slot if (!moveItemStackTo(clickedStack, 1, 2, false)) { return ItemStack.EMPTY; } @@ -161,18 +100,4 @@ public class MechanicalSieveMenu extends EContainerMenu { return stack; } - - @Override - public boolean clickMenuButton(Player player, int id) { - if (0 <= id && id < 3) { - this.sieve.setRedstoneMode(id); - return false; - } - return false; - } - - @Override - public boolean stillValid(Player player) { - return this.sieve.stillValid(player); - } } diff --git a/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java b/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java index a9658ac2..5b76fcdf 100644 --- a/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java @@ -23,7 +23,7 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.player.Player; import thedarkcolour.exdeorum.blockentity.EBlockEntity; import thedarkcolour.exdeorum.client.ClientHandler; -import thedarkcolour.exdeorum.menu.EContainerMenu; +import thedarkcolour.exdeorum.menu.AbstractMachineMenu; public class ClientMessageHandler { public static boolean isInVoidWorld; @@ -54,7 +54,7 @@ public class ClientMessageHandler { public static void handleMenuProperty(MenuPropertyMessage msg) { Player player = Minecraft.getInstance().player; - if (player != null && player.containerMenu instanceof EContainerMenu menu && menu.containerId == msg.containerId()) { + if (player != null && player.containerMenu instanceof AbstractMachineMenu menu && menu.containerId == msg.containerId()) { menu.setClientProperty(msg.index(), msg.value()); } } diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java index 423d42b8..63fba00f 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java @@ -24,6 +24,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.TagKey; import net.minecraft.util.GsonHelper; import net.minecraft.world.Container; @@ -36,7 +37,9 @@ import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootDataType; +import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.providers.number.*; import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.fluids.FluidStack; @@ -55,6 +58,7 @@ import thedarkcolour.exdeorum.registry.ERecipeTypes; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; public final class RecipeUtil { @@ -298,7 +302,12 @@ public final class RecipeUtil { } } + @SuppressWarnings("deprecation") public static boolean isTagEmpty(TagKey tag) { return BuiltInRegistries.ITEM.getTag(tag).map(set -> !set.iterator().hasNext()).orElse(PreferredOres.getPreferredOre(tag) == Items.AIR); } + + public static LootContext emptyLootContext(ServerLevel level) { + return new LootContext.Builder(new LootParams(level, Map.of(), Map.of(), 0)).create(null); + } } diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java index 8a272197..76a89d5c 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java @@ -87,6 +87,7 @@ public class SieveRecipe extends ProbabilityRecipe { return new SieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly); } + @SuppressWarnings("deprecation") @Override public @Nullable SieveRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { Ingredient ingredient = Ingredient.fromNetwork(buffer); @@ -96,6 +97,7 @@ public class SieveRecipe extends ProbabilityRecipe { return new SieveRecipe(id, ingredient, mesh, result, resultAmount, buffer.readBoolean()); } + @SuppressWarnings("deprecation") @Override public void toNetwork(FriendlyByteBuf buffer, SieveRecipe recipe) { recipe.getIngredient().toNetwork(buffer); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java b/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java index d8da56c8..07fb03fe 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java @@ -26,6 +26,7 @@ import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity; import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; +import thedarkcolour.exdeorum.blockentity.MechanicalHammerBlockEntity; import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.SieveBlockEntity; import thedarkcolour.exdeorum.blockentity.WaterCrucibleBlockEntity; @@ -156,4 +157,5 @@ public class EBlockEntities { EBlocks.CRYSTALLIZED_SIEVE.get() ).build(null)); public static final RegistryObject> MECHANICAL_SIEVE = BLOCK_ENTITIES.register("mechanical_sieve", () -> BlockEntityType.Builder.of(MechanicalSieveBlockEntity::new, EBlocks.MECHANICAL_SIEVE.get()).build(null)); + public static final RegistryObject> MECHANICAL_HAMMER = BLOCK_ENTITIES.register("mechanical_hammer", () -> BlockEntityType.Builder.of(MechanicalHammerBlockEntity::new, EBlocks.MECHANICAL_HAMMER.get()).build(null)); } diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java index 1c62f472..4302c8d9 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java @@ -121,6 +121,8 @@ public class EBlocks { public static final RegistryObject CRYSTALLIZED_SIEVE = registerSieve("crystallized_sieve", SoundType.GLASS); // Mechanical Sieve public static final RegistryObject MECHANICAL_SIEVE = BLOCKS.register("mechanical_sieve", () -> new MechanicalSieveBlock(of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().strength(5f, 1200f))); + // Mechanical Hammer + public static final RegistryObject MECHANICAL_HAMMER = BLOCKS.register("mechanical_hammer", () -> new MechanicalHammerBlock(of())); // Lava Crucibles public static final RegistryObject PORCELAIN_CRUCIBLE = registerLavaCrucible("porcelain_crucible", true, SoundType.STONE); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EItems.java b/src/main/java/thedarkcolour/exdeorum/registry/EItems.java index 03df6a7b..778ec879 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EItems.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EItems.java @@ -225,6 +225,7 @@ public class EItems { public static final RegistryObject CRYSTALLIZED_SIEVE = registerItemBlock(EBlocks.CRYSTALLIZED_SIEVE); // Mechanical Sieves public static final RegistryObject MECHANICAL_SIEVE = registerItemBlock(EBlocks.MECHANICAL_SIEVE); + public static final RegistryObject MECHANICAL_HAMMER = registerItemBlock(EBlocks.MECHANICAL_HAMMER); // Lava Crucibles public static final RegistryObject PORCELAIN_CRUCIBLE = registerItemBlock(EBlocks.PORCELAIN_CRUCIBLE); @@ -365,6 +366,7 @@ public class EItems { output.accept(CRYSTALLIZED_SIEVE.get()); } output.accept(MECHANICAL_SIEVE.get()); + output.accept(MECHANICAL_HAMMER.get()); output.accept(PORCELAIN_CRUCIBLE.get()); output.accept(WARPED_CRUCIBLE.get()); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java b/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java index a8750394..f4d95353 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java @@ -25,10 +25,12 @@ import net.minecraftforge.network.IContainerFactory; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.blockentity.MechanicalHammerMenu; import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; public class EMenus { public static final DeferredRegister> MENUS = DeferredRegister.create(Registries.MENU, ExDeorum.ID); public static final RegistryObject> MECHANICAL_SIEVE = MENUS.register("mechanical_sieve", () -> new MenuType<>((IContainerFactory) MechanicalSieveMenu::new, FeatureFlags.DEFAULT_FLAGS)); + public static final RegistryObject> MECHANICAL_HAMMER = MENUS.register("mechanical_hammer", () -> new MenuType<>((IContainerFactory) MechanicalHammerMenu::new, FeatureFlags.DEFAULT_FLAGS)); } diff --git a/src/main/resources/assets/exdeorum/blockstates/mechanical_hammer.json b/src/main/resources/assets/exdeorum/blockstates/mechanical_hammer.json new file mode 100644 index 00000000..c37f7512 --- /dev/null +++ b/src/main/resources/assets/exdeorum/blockstates/mechanical_hammer.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=east,running=false": { + "model": "exdeorum:block/mechanical_hammer_off", + "y": 90 + }, + "facing=east,running=true": { + "model": "exdeorum:block/mechanical_hammer_on", + "y": 90 + }, + "facing=north,running=false": { + "model": "exdeorum:block/mechanical_hammer_off" + }, + "facing=north,running=true": { + "model": "exdeorum:block/mechanical_hammer_on" + }, + "facing=south,running=false": { + "model": "exdeorum:block/mechanical_hammer_off", + "y": 180 + }, + "facing=south,running=true": { + "model": "exdeorum:block/mechanical_hammer_on", + "y": 180 + }, + "facing=west,running=false": { + "model": "exdeorum:block/mechanical_hammer_off", + "y": 270 + }, + "facing=west,running=true": { + "model": "exdeorum:block/mechanical_hammer_on", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_off.json b/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_off.json new file mode 100644 index 00000000..139ca47a --- /dev/null +++ b/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_off.json @@ -0,0 +1,9 @@ +{ + "parent": "minecraft:block/orientable_with_bottom", + "textures": { + "front": "exdeorum:block/mechanical_hammer_front", + "side": "exdeorum:block/mechanical_hammer_side", + "top": "exdeorum:block/mechanical_hammer_top", + "bottom": "exdeorum:block/mechanical_hammer_bottom" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_on.json b/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_on.json new file mode 100644 index 00000000..0f2af075 --- /dev/null +++ b/src/main/resources/assets/exdeorum/models/block/mechanical_hammer_on.json @@ -0,0 +1,9 @@ +{ + "parent": "minecraft:block/orientable_with_bottom", + "textures": { + "front": "exdeorum:block/mechanical_hammer_front_on", + "side": "exdeorum:block/mechanical_hammer_side", + "top": "exdeorum:block/mechanical_hammer_top", + "bottom": "exdeorum:block/mechanical_hammer_bottom" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_bottom.png b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_bottom.png new file mode 100644 index 00000000..6fb79de0 Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_bottom.png differ diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front.png b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front.png new file mode 100644 index 00000000..14076e8e Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front.png differ diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front_on.png b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front_on.png new file mode 100644 index 00000000..63ff5f64 Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_front_on.png differ diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_side.png b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_side.png new file mode 100644 index 00000000..6d161971 Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_side.png differ diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.pdn b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.pdn new file mode 100644 index 00000000..e46a32ea Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.pdn differ diff --git a/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.png b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.png new file mode 100644 index 00000000..44d2a58a Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/block/mechanical_hammer_top.png differ diff --git a/src/main/resources/assets/exdeorum/textures/gui/container/mechanical_hammer.png b/src/main/resources/assets/exdeorum/textures/gui/container/mechanical_hammer.png new file mode 100644 index 00000000..3cc69158 Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/gui/container/mechanical_hammer.png differ diff --git a/src/main/resources/assets/exdeorum/textures/item/empty_slot_hammer.png b/src/main/resources/assets/exdeorum/textures/item/empty_slot_hammer.png new file mode 100644 index 00000000..621a616f Binary files /dev/null and b/src/main/resources/assets/exdeorum/textures/item/empty_slot_hammer.png differ