Merge pull request #40 from thedarkcolour/mechanical-hammer

Mechanical hammer
This commit is contained in:
thedarkcolour 2024-02-01 18:58:03 -08:00 committed by GitHub
commit faeef9fc36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 1240 additions and 292 deletions

View File

@ -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 105d8a61ea7145d7798146d385d4aad24fd1588d data/exdeorum/loot_tables/blocks/acacia_barrel.json
1e77127a82cbba0937bb02694f65cf1893aeffcb data/exdeorum/loot_tables/blocks/acacia_crucible.json 1e77127a82cbba0937bb02694f65cf1893aeffcb data/exdeorum/loot_tables/blocks/acacia_crucible.json
fcc00910a8cc94bed6339d6833fcec53c501a0d7 data/exdeorum/loot_tables/blocks/acacia_sieve.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 475b89fd8f09834652f80c93d8a6d0964d708ead data/exdeorum/loot_tables/blocks/maple_barrel.json
54f36187d7fb97dedc4680d14e2ad7d70b5c64af data/exdeorum/loot_tables/blocks/maple_crucible.json 54f36187d7fb97dedc4680d14e2ad7d70b5c64af data/exdeorum/loot_tables/blocks/maple_crucible.json
7ffe80360af055f3977d05b5684a299886bcb756 data/exdeorum/loot_tables/blocks/maple_sieve.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 a84508222cb36b07cb20ee31915d802bcc411149 data/exdeorum/loot_tables/blocks/mechanical_sieve.json
cad973c873a2e50ccfac91e88eadb3c2462d39d1 data/exdeorum/loot_tables/blocks/oak_barrel.json cad973c873a2e50ccfac91e88eadb3c2462d39d1 data/exdeorum/loot_tables/blocks/oak_barrel.json
f94bc97efbfd26ccf7dba32d414fb5e33decd5f6 data/exdeorum/loot_tables/blocks/oak_crucible.json f94bc97efbfd26ccf7dba32d414fb5e33decd5f6 data/exdeorum/loot_tables/blocks/oak_crucible.json

View File

@ -1,2 +1,2 @@
// 1.20.1 2024-01-26T16:29:51.8613552 ModKit Language: en_us for mod 'exdeorum' // 1.20.1 2024-01-25T14:16:46.2316734 ModKit Language: en_us for mod 'exdeorum'
2f38e208308fb2a7f506e2ce2b400970d1c27b57 assets/exdeorum/lang/en_us.json 248ab1a837b9857fb1928e826cb02f9f142e8d96 assets/exdeorum/lang/en_us.json

View File

@ -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 4ba3bb2c6174ac3728a4b85e34681f118ec8eb34 assets/exdeorum/models/item/acacia_barrel.json
c03ce41f7c071498fcbd5f5225e91dcb2f365fbb assets/exdeorum/models/item/acacia_crucible.json c03ce41f7c071498fcbd5f5225e91dcb2f365fbb assets/exdeorum/models/item/acacia_crucible.json
3b4f1d45c0d9c4cd1d9a5cdf6ddc8d2c9791bca5 assets/exdeorum/models/item/acacia_sieve.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 c3f2af2a88cd97148b05efbd6e24fc2558fcc0b8 assets/exdeorum/models/item/maple_barrel.json
cc045825c562e9133858ce5cfe6e6f1dcb747d8e assets/exdeorum/models/item/maple_crucible.json cc045825c562e9133858ce5cfe6e6f1dcb747d8e assets/exdeorum/models/item/maple_crucible.json
a64e9b9ce91ac6b2f36690a770afc52b8900a614 assets/exdeorum/models/item/maple_sieve.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 1e2b482f5fc4d283f5ca12919b575f86dc4a9541 assets/exdeorum/models/item/mechanical_sieve.json
d543d3e18bdcf2bf79a762b52cc61a4161124db1 assets/exdeorum/models/item/mycelium_spores.json d543d3e18bdcf2bf79a762b52cc61a4161124db1 assets/exdeorum/models/item/mycelium_spores.json
1f48b2ce3452ce5d02142c9f663ae7bdb7d1d934 assets/exdeorum/models/item/netherite_hammer.json 1f48b2ce3452ce5d02142c9f663ae7bdb7d1d934 assets/exdeorum/models/item/netherite_hammer.json

View File

@ -84,6 +84,7 @@
"block.exdeorum.maple_barrel": "Maple Barrel", "block.exdeorum.maple_barrel": "Maple Barrel",
"block.exdeorum.maple_crucible": "Maple Crucible", "block.exdeorum.maple_crucible": "Maple Crucible",
"block.exdeorum.maple_sieve": "Maple Sieve", "block.exdeorum.maple_sieve": "Maple Sieve",
"block.exdeorum.mechanical_hammer": "Mechanical Hammer",
"block.exdeorum.mechanical_sieve": "Mechanical Sieve", "block.exdeorum.mechanical_sieve": "Mechanical Sieve",
"block.exdeorum.oak_barrel": "Oak Barrel", "block.exdeorum.oak_barrel": "Oak Barrel",
"block.exdeorum.oak_crucible": "Oak Crucible", "block.exdeorum.oak_crucible": "Oak Crucible",
@ -118,6 +119,7 @@
"block.exdeorum.willow_crucible": "Willow Crucible", "block.exdeorum.willow_crucible": "Willow Crucible",
"block.exdeorum.willow_sieve": "Willow Sieve", "block.exdeorum.willow_sieve": "Willow Sieve",
"block.exdeorum.witch_water": "Witch Water", "block.exdeorum.witch_water": "Witch Water",
"exdeorum.container.mechanical_hammer": "Mechanical Hammer",
"exdeorum.container.mechanical_sieve": "Mechanical Sieve", "exdeorum.container.mechanical_sieve": "Mechanical Sieve",
"fluid_type.exdeorum.witch_water": "Witch Water", "fluid_type.exdeorum.witch_water": "Witch Water",
"generator.exdeorum.void_world": "Void World", "generator.exdeorum.void_world": "Void World",
@ -145,6 +147,7 @@
"gui.exdeorum.redstone_control.unpowered": "Unpowered", "gui.exdeorum.redstone_control.unpowered": "Unpowered",
"info.exdeorum.crimson_nylium_spores": "Use on netherrack to turn it into a crimson nylium block.", "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.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.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.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.", "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.",

View File

@ -0,0 +1,3 @@
{
"parent": "exdeorum:block/mechanical_hammer_off"
}

View File

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

View File

@ -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"
}

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Block, BlockState> builder) {
builder.add(RUNNING, FACING);
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
return type == EBlockEntities.MECHANICAL_HAMMER.get() && !level.isClientSide ? (BlockEntityTicker<T>) new MechanicalHammerBlockEntity.ServerTicker<>() : null;
}
@Override
public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List<Component> 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);
}
}
}

View File

@ -69,7 +69,7 @@ public class MechanicalSieveBlock extends EBlock {
@Nullable @Nullable
@Override @Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState pState, BlockEntityType<T> type) { public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState pState, BlockEntityType<T> type) {
return type == EBlockEntities.MECHANICAL_SIEVE.get() && !level.isClientSide ? (BlockEntityTicker<T>) new MechanicalSieveBlockEntity.ServerTicker() : null; return type == EBlockEntities.MECHANICAL_SIEVE.get() && !level.isClientSide ? (BlockEntityTicker<T>) new MechanicalSieveBlockEntity.ServerTicker<>() : null;
} }
@Override @Override
@ -93,7 +93,7 @@ public class MechanicalSieveBlock extends EBlock {
public void playerWillDestroy(Level level, BlockPos pos, BlockState pState, Player player) { public void playerWillDestroy(Level level, BlockPos pos, BlockState pState, Player player) {
if (!level.isClientSide && player.isCreative() && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) { if (!level.isClientSide && player.isCreative() && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) {
if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) { if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) {
if (!sieve.getMesh().isEmpty()) { if (!sieve.getLogic().getMesh().isEmpty()) {
var stack = new ItemStack(this); var stack = new ItemStack(this);
BlockItem.setBlockEntityData(stack, EBlockEntities.MECHANICAL_SIEVE.get(), sieve.saveWithoutMetadata()); 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); var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, stack);

View File

@ -57,7 +57,7 @@ public class SieveBlock extends EBlock {
if (!level.isClientSide) { if (!level.isClientSide) {
if (!state.is(newState.getBlock())) { if (!state.is(newState.getBlock())) {
if (level.getBlockEntity(pos) instanceof SieveBlockEntity sieve) { if (level.getBlockEntity(pos) instanceof SieveBlockEntity sieve) {
var mesh = sieve.getMesh(); var mesh = sieve.getLogic().getMesh();
if (!mesh.isEmpty()) { if (!mesh.isEmpty()) {
dropItem(level, pos, mesh); dropItem(level, pos, mesh);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<M extends AbstractMachineBlockEntity<M>> 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<ItemHelper> capabilityInventory;
private final LazyOptional<EnergyStorage> capabilityEnergy;
@SuppressWarnings("unchecked")
public AbstractMachineBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state, Function<M, ItemHelper> 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 <T> LazyOptional<T> getCapability(@Nonnull Capability<T> 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<M extends AbstractMachineBlockEntity<M>> implements BlockEntityTicker<M> {
@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();
}
}
}
}
}

View File

@ -63,19 +63,9 @@ public abstract class AbstractSieveBlockEntity extends EBlockEntity implements S
this.logic.loadNbt(nbt); this.logic.loadNbt(nbt);
} }
// Used for rendering and for TOP @Override
public ItemStack getMesh() { public SieveLogic getLogic() {
return this.logic.getMesh(); return this.logic;
}
// Used for rendering
public float getProgress() {
return this.logic.getProgress();
}
// Used for rendering
public ItemStack getContents() {
return this.logic.getContents();
} }
@Override @Override

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MechanicalHammerBlockEntity> {
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;
}
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MechanicalHammerBlockEntity> {
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);
}
}
}

View File

@ -19,35 +19,19 @@
package thedarkcolour.exdeorum.blockentity; package thedarkcolour.exdeorum.blockentity;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel; 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.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.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack; 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.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.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.helper.ItemHelper;
import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; import thedarkcolour.exdeorum.blockentity.logic.SieveLogic;
import thedarkcolour.exdeorum.client.screen.RedstoneControlWidget;
import thedarkcolour.exdeorum.config.EConfig; import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; import thedarkcolour.exdeorum.menu.MechanicalSieveMenu;
@ -55,107 +39,70 @@ import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EBlockEntities; import thedarkcolour.exdeorum.registry.EBlockEntities;
import thedarkcolour.exdeorum.tag.EItemTags; import thedarkcolour.exdeorum.tag.EItemTags;
import javax.annotation.Nonnull; public class MechanicalSieveBlockEntity extends AbstractMachineBlockEntity<MechanicalSieveBlockEntity> implements SieveLogic.Owner {
public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity implements MenuProvider {
private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE); private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE);
private static final int INPUT_SLOT = 0; private static final int INPUT_SLOT = 0;
public static final int MESH_SLOT = 1; public static final int MESH_SLOT = 1;
public final ItemHelper inventory; private final SieveLogic logic;
public final EnergyHelper energy;
private int redstoneMode;
// not saved to NBT
public boolean hasRedstonePower;
private final LazyOptional<ItemHelper> capabilityInventory;
private final LazyOptional<EnergyStorage> capabilityEnergy;
public MechanicalSieveBlockEntity(BlockPos pos, BlockState state) { 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.logic = new SieveLogic(this, false, true);
this.energy = new EnergyHelper(40000);
this.capabilityInventory = LazyOptional.of(() -> this.inventory);
this.capabilityEnergy = LazyOptional.of(() -> this.energy);
} }
@Override @Override
protected void saveAdditional(CompoundTag nbt) { protected void saveAdditional(CompoundTag nbt) {
super.saveAdditional(nbt); super.saveAdditional(nbt);
nbt.put("inventory", this.inventory.serializeNBT()); this.logic.saveNbt(nbt);
nbt.putInt("energy", this.energy.getEnergyStored());
nbt.putInt("redstoneMode", this.redstoneMode);
} }
@Override @Override
public void load(CompoundTag nbt) { public void load(CompoundTag nbt) {
super.load(nbt); super.load(nbt);
this.inventory.deserializeNBT(nbt.getCompound("inventory")); this.logic.loadNbt(nbt);
this.energy.setStoredEnergy(nbt.getInt("energy"));
this.redstoneMode = Mth.clamp(nbt.getInt("redstoneMode"), 0, 2);
} }
@Override @Override
public void onLoad() { protected boolean isRunning() {
checkPoweredState(this.level, this.worldPosition); return !this.logic.getContents().isEmpty();
} }
@Override @Override
public InteractionResult use(Level level, Player player, InteractionHand hand) { protected void tryStartRunning() {
if (player instanceof ServerPlayer serverPlayer) { var input = this.inventory.getStackInSlot(INPUT_SLOT);
NetworkHooks.openScreen(serverPlayer, this, buffer -> {
buffer.writeBlockPos(getBlockPos()); if (this.logic.isValidInput(input)) {
buffer.writeByte(this.redstoneMode); this.logic.startSifting(AbstractSieveBlockEntity.singleCopy(input));
}); input.shrink(1);
return InteractionResult.CONSUME;
} else {
return InteractionResult.SUCCESS;
} }
} }
@Nonnull
@Override @Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @javax.annotation.Nullable Direction side) { protected void runMachineTick() {
if (cap == ForgeCapabilities.ENERGY) { this.logic.sift(0.01f);
return this.capabilityEnergy.cast();
} else if (cap == ForgeCapabilities.ITEM_HANDLER) {
return this.capabilityInventory.cast();
}
return super.getCapability(cap, side);
} }
@Override @Override
public void invalidateCaps() { protected int getEnergyConsumption() {
super.invalidateCaps(); return EConfig.SERVER.mechanicalSieveEnergyConsumption.get();
this.capabilityEnergy.invalidate();
this.capabilityInventory.invalidate();
} }
private void serverTick() { @Override
if (this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_IGNORED || ((this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_UNPOWERED)) != this.hasRedstonePower) { public void writeVisualData(FriendlyByteBuf buffer) {
var energyConsumption = EConfig.SERVER.mechanicalSieveEnergyConsumption.get(); buffer.writeItem(this.logic.getMesh());
buffer.writeFloat(this.logic.getProgress());
buffer.writeItem(this.logic.getContents());
}
if (this.energy.getEnergyStored() >= energyConsumption) { @Override
if (this.logic.getContents().isEmpty()) { public void readVisualData(FriendlyByteBuf buffer) {
var input = this.inventory.getStackInSlot(INPUT_SLOT); this.logic.setMesh(buffer.readItem(), false);
this.logic.setProgress(buffer.readFloat());
if (this.logic.isValidInput(input)) { this.logic.setContents(buffer.readItem());
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 @Override
@ -205,43 +152,32 @@ public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity impleme
return TITLE; return TITLE;
} }
@Nullable
@Override @Override
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player pPlayer) { public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player pPlayer) {
return new MechanicalSieveMenu(containerId, playerInventory, this); return new MechanicalSieveMenu(containerId, playerInventory, this);
} }
public boolean stillValid(Player player) { @Override
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;
}
}
public SieveLogic getLogic() { public SieveLogic getLogic() {
return this.logic; return this.logic;
} }
public void setRedstoneMode(int redstoneMode) { @SuppressWarnings("DataFlowIssue")
this.redstoneMode = redstoneMode; @Override
public ServerLevel getServerLevel() {
return (ServerLevel) this.level;
} }
public int getRedstoneMode() { private static class ItemHandler extends ItemHelper {
return this.redstoneMode; private final MechanicalSieveBlockEntity sieve;
}
public void checkPoweredState(Level level, BlockPos pos) { public ItemHandler(MechanicalSieveBlockEntity sieve) {
this.hasRedstonePower = level.hasNeighborSignal(pos); super(22);
} this.sieve = sieve;
private class ItemHandler extends ItemHelper {
public ItemHandler(int size) {
super(size);
} }
@Override @Override
public boolean isItemValid(int slot, @NotNull ItemStack stack) { public boolean isItemValid(int slot, ItemStack stack) {
if (slot == INPUT_SLOT) { if (slot == INPUT_SLOT) {
return !RecipeUtil.getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty(); return !RecipeUtil.getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty();
} else if (slot == MESH_SLOT) { } else if (slot == MESH_SLOT) {
@ -264,20 +200,13 @@ public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity impleme
@Override @Override
protected void onContentsChanged(int slot) { protected void onContentsChanged(int slot) {
if (slot == MESH_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 @Override
protected void onLoad() { protected void onLoad() {
MechanicalSieveBlockEntity.this.logic.setMesh(MechanicalSieveBlockEntity.this.inventory.getStackInSlot(MESH_SLOT), false); this.sieve.logic.setMesh(this.sieve.inventory.getStackInSlot(MESH_SLOT), false);
}
}
public static class ServerTicker implements BlockEntityTicker<MechanicalSieveBlockEntity> {
@Override
public void tick(Level level, BlockPos pos, BlockState state, MechanicalSieveBlockEntity sieve) {
sieve.serverTick();
} }
} }
} }

View File

@ -27,14 +27,11 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.LootParams;
import thedarkcolour.exdeorum.config.EConfig; import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.tag.EItemTags; import thedarkcolour.exdeorum.tag.EItemTags;
import java.util.Map;
public class SieveLogic { public class SieveLogic {
private final Owner owner; private final Owner owner;
private final boolean saveMesh; private final boolean saveMesh;
@ -80,7 +77,7 @@ public class SieveLogic {
// Need epsilon because floating point decimals suck // Need epsilon because floating point decimals suck
if (this.progress >= 1.0f - Mth.EPSILON) { if (this.progress >= 1.0f - Mth.EPSILON) {
var level = this.owner.getServerLevel(); 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 rand = level.random;
var limitDrops = this.contents.getItem() == Items.MOSS_BLOCK && EConfig.SERVER.limitMossSieveDrops.get(); var limitDrops = this.contents.getItem() == Items.MOSS_BLOCK && EConfig.SERVER.limitMossSieveDrops.get();
var handledAnyDrops = false; var handledAnyDrops = false;
@ -207,5 +204,7 @@ public class SieveLogic {
boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand); boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand);
void markUpdated(); void markUpdated();
SieveLogic getLogic();
} }
} }

View File

@ -43,6 +43,7 @@ import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
import thedarkcolour.exdeorum.client.ter.BarrelRenderer; import thedarkcolour.exdeorum.client.ter.BarrelRenderer;
import thedarkcolour.exdeorum.client.ter.CrucibleRenderer; import thedarkcolour.exdeorum.client.ter.CrucibleRenderer;
@ -101,6 +102,7 @@ public class ClientHandler {
event.enqueueWork(() -> { event.enqueueWork(() -> {
setRenderLayers(); setRenderLayers();
MenuScreens.register(EMenus.MECHANICAL_SIEVE.get(), MechanicalSieveScreen::new); MenuScreens.register(EMenus.MECHANICAL_SIEVE.get(), MechanicalSieveScreen::new);
MenuScreens.register(EMenus.MECHANICAL_HAMMER.get(), MechanicalHammerScreen::new);
}); });
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MechanicalHammerMenu> {
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);
}
}
}

View File

@ -40,14 +40,12 @@ public class MechanicalSieveScreen extends AbstractContainerScreen<MechanicalSie
public static final int RECIPE_CLICK_AREA_WIDTH = 21; public static final int RECIPE_CLICK_AREA_WIDTH = 21;
public static final int RECIPE_CLICK_AREA_HEIGHT = 14; public static final int RECIPE_CLICK_AREA_HEIGHT = 14;
private final MechanicalSieveMenu menu;
@Nullable @Nullable
private RedstoneControlWidget redstoneControlWidget; private RedstoneControlWidget redstoneControlWidget;
public MechanicalSieveScreen(MechanicalSieveMenu menu, Inventory playerInventory, Component title) { public MechanicalSieveScreen(MechanicalSieveMenu menu, Inventory playerInventory, Component title) {
super(menu, playerInventory, title); super(menu, playerInventory, title);
this.menu = menu;
this.imageWidth = 176; this.imageWidth = 176;
this.imageHeight = 173; this.imageHeight = 173;
this.inventoryLabelY += 7; this.inventoryLabelY += 7;
@ -57,7 +55,7 @@ public class MechanicalSieveScreen extends AbstractContainerScreen<MechanicalSie
protected void init() { protected void init() {
super.init(); super.init();
this.redstoneControlWidget = new RedstoneControlWidget(this, BACKGROUND_TEXTURE, this.leftPos + 176, this.topPos + 3); this.redstoneControlWidget = new RedstoneControlWidget(this.menu, BACKGROUND_TEXTURE, this.leftPos + this.imageWidth, this.topPos + 3);
addRenderableWidget(this.redstoneControlWidget); addRenderableWidget(this.redstoneControlWidget);
} }
@ -73,12 +71,12 @@ public class MechanicalSieveScreen extends AbstractContainerScreen<MechanicalSie
graphics.blit(BACKGROUND_TEXTURE, left, top, 0, 0, this.imageWidth, this.imageHeight); graphics.blit(BACKGROUND_TEXTURE, left, top, 0, 0, this.imageWidth, this.imageHeight);
// energy bar // energy bar
int energy = Mth.floor(54 * ((float) this.menu.prevSieveEnergy / EConfig.SERVER.mechanicalSieveEnergyStorage.get())); int energy = Mth.floor(54 * ((float) this.menu.prevEnergy / EConfig.SERVER.mechanicalSieveEnergyStorage.get()));
graphics.blit(BACKGROUND_TEXTURE, left + 10, top + 22 + 54 - energy, this.imageWidth, 14 + 54 - energy, 12, energy); graphics.blit(BACKGROUND_TEXTURE, left + 10, top + 22 + 54 - energy, this.imageWidth, 14 + 54 - energy, 12, energy);
// progress arrow // progress arrow
int progress = Math.min(21, (int) (this.menu.sieve.getProgress() * 22)); int progress = Math.min(21, (int) (this.menu.machine.getLogic().getProgress() * 22));
graphics.blit(BACKGROUND_TEXTURE, left + 51, top + 42, this.imageWidth, 0, progress, 14); graphics.blit(BACKGROUND_TEXTURE, left + RECIPE_CLICK_AREA_POS_X, top + RECIPE_CLICK_AREA_POS_Y, this.imageWidth, 0, progress, 14);
} }
@Override @Override
@ -91,7 +89,7 @@ public class MechanicalSieveScreen extends AbstractContainerScreen<MechanicalSie
int ry = my - this.topPos; int ry = my - this.topPos;
if (9 <= rx && rx < 23 && 21 <= ry && ry < 77) { if (9 <= rx && rx < 23 && 21 <= ry && ry < 77) {
var energyTooltip = Component.translatable(TranslationKeys.ENERGY).append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, this.menu.prevSieveEnergy, EConfig.SERVER.mechanicalSieveEnergyStorage.get())).append(" FE"); 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); graphics.renderTooltip(Minecraft.getInstance().font, energyTooltip, mx, my);
} }
} }

View File

@ -31,6 +31,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.menu.AbstractMachineMenu;
import java.util.List; import java.util.List;
@ -46,7 +47,7 @@ public class RedstoneControlWidget implements GuiEventListener, NarratableEntry,
}; };
private static final Component REDSTONE_CONTROL_LABEL = Component.translatable(TranslationKeys.REDSTONE_CONTROL_LABEL); private static final Component REDSTONE_CONTROL_LABEL = Component.translatable(TranslationKeys.REDSTONE_CONTROL_LABEL);
private final MechanicalSieveScreen screen; private final AbstractMachineMenu<?> screen;
private final ResourceLocation texture; private final ResourceLocation texture;
private final int posX; private final int posX;
private final int posY; 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 // Last time (from currentTimeMillis) this button was clicked, used in animation lerp
private long lastClicked = -1L; 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.screen = screen;
this.texture = texture; this.texture = texture;
this.posX = posX; this.posX = posX;
@ -106,7 +107,7 @@ public class RedstoneControlWidget implements GuiEventListener, NarratableEntry,
var font = Minecraft.getInstance().font; var font = Minecraft.getInstance().font;
if (this.expanded) { 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); graphics.blit(this.texture, this.posX, this.posY, this.expandedU, this.expandedV, this.expandedWidth, this.expandedHeight);
for (int i = 0; i < 3; ++i) { 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); 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; return false;
} }
@SuppressWarnings("DataFlowIssue")
private void setRedstoneMode(int redstoneMode) { private void setRedstoneMode(int redstoneMode) {
this.screen.getMenu().clickMenuButton(Minecraft.getInstance().player, redstoneMode); this.screen.clickMenuButton(Minecraft.getInstance().player, redstoneMode);
Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.screen.getMenu().containerId, redstoneMode); Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.screen.containerId, redstoneMode);
} }
private void drawPartialConfig(GuiGraphics graphics) { private void drawPartialConfig(GuiGraphics graphics) {

View File

@ -23,31 +23,33 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraftforge.registries.ForgeRegistries; import thedarkcolour.exdeorum.blockentity.EBlockEntity;
import thedarkcolour.exdeorum.blockentity.AbstractSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.logic.SieveLogic;
import thedarkcolour.exdeorum.client.RenderUtil; import thedarkcolour.exdeorum.client.RenderUtil;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class SieveRenderer<T extends AbstractSieveBlockEntity> implements BlockEntityRenderer<T> { public class SieveRenderer<T extends EBlockEntity & SieveLogic.Owner> implements BlockEntityRenderer<T> {
public static final Map<Item, TextureAtlasSprite> MESH_TEXTURES = new HashMap<>(); public static final Map<Item, TextureAtlasSprite> MESH_TEXTURES = new HashMap<>();
@Override @Override
public void render(T sieve, float partialTicks, PoseStack stack, MultiBufferSource buffers, int light, int overlay) { 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) { if (!contents.isEmpty() && contents.getItem() instanceof BlockItem blockItem) {
var block = blockItem.getBlock(); var block = blockItem.getBlock();
var percentage = sieve.getProgress(); var percentage = logic.getProgress();
var face = RenderUtil.getTopFace(block); var face = RenderUtil.getTopFace(block);
face.renderFlatSpriteLerp(buffers, stack, percentage, 0xff, 0xff, 0xff, light, 1.0f, 15f, 13f); face.renderFlatSpriteLerp(buffers, stack, percentage, 0xff, 0xff, 0xff, light, 1.0f, 15f, 13f);
} }
var mesh = sieve.getMesh(); var mesh = logic.getMesh();
if (!mesh.isEmpty()) { if (!mesh.isEmpty()) {
var builder = buffers.getBuffer(RenderType.cutoutMipped()); var builder = buffers.getBuffer(RenderType.cutoutMipped());
@ -57,7 +59,8 @@ public class SieveRenderer<T extends AbstractSieveBlockEntity> implements BlockE
if (MESH_TEXTURES.containsKey(meshItem)) { if (MESH_TEXTURES.containsKey(meshItem)) {
meshSprite = MESH_TEXTURES.get(meshItem); meshSprite = MESH_TEXTURES.get(meshItem);
} else { } else {
ResourceLocation registryName = ForgeRegistries.ITEMS.getKey(meshItem); @SuppressWarnings("deprecation")
ResourceLocation registryName = BuiltInRegistries.ITEM.getKey(meshItem);
ResourceLocation textureLoc = registryName.withPrefix("item/mesh/"); ResourceLocation textureLoc = registryName.withPrefix("item/mesh/");
meshSprite = RenderUtil.blockAtlas.getSprite(textureLoc); meshSprite = RenderUtil.blockAtlas.getSprite(textureLoc);
MESH_TEXTURES.put(meshItem, meshSprite); MESH_TEXTURES.put(meshItem, meshSprite);

View File

@ -48,6 +48,7 @@ import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModList;
import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity;
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
import thedarkcolour.exdeorum.compat.GroupedSieveRecipe; import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
import thedarkcolour.exdeorum.compat.ModIds; 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.IRON_HAMMER.get()), HAMMER);
registration.addRecipeCatalyst(new ItemStack(EItems.DIAMOND_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.NETHERITE_HAMMER.get()), HAMMER);
registration.addRecipeCatalyst(new ItemStack(EItems.MECHANICAL_HAMMER.get()), HAMMER);
} }
@Override @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.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.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_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<ItemStack>(); var toRemove = new ArrayList<ItemStack>();
@ -322,6 +325,22 @@ public class ExDeorumJeiPlugin implements IModPlugin {
return List.of(); return List.of();
} }
}); });
registration.addGuiContainerHandler(MechanicalHammerScreen.class, new IGuiContainerHandler<>() {
@Override
public Collection<IGuiClickableArea> 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<Rect2i> getGuiExtraAreas(MechanicalHammerScreen containerScreen) {
var widget = containerScreen.getRedstoneControlWidget();
if (widget != null) {
return widget.getJeiBounds();
}
return List.of();
}
});
} }
private static <C extends Container, T extends Recipe<C>> void addRecipes(IRecipeRegistration registration, RecipeType<T> category, Supplier<net.minecraft.world.item.crafting.RecipeType<T>> type) { private static <C extends Container, T extends Recipe<C>> void addRecipes(IRecipeRegistration registration, RecipeType<T> category, Supplier<net.minecraft.world.item.crafting.RecipeType<T>> type) {

View File

@ -80,14 +80,15 @@ public class ExDeorumInfoProvider implements IProbeInfoProvider {
info.tank(crucible.getTank()); info.tank(crucible.getTank());
} }
} else if (te instanceof SieveBlockEntity sieve) { } else if (te instanceof SieveBlockEntity sieve) {
if (!sieve.getContents().isEmpty()) { var logic = sieve.getLogic();
info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text((Math.round(1000 * sieve.getProgress()) / 10) + "%")); 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()) { if (playerEntity.isShiftKeyDown()) {
var mesh = sieve.getMesh(); var mesh = logic.getMesh();
info.horizontal(info.defaultLayoutStyle().spacing(10).alignment(ElementAlignment.ALIGN_CENTER)) info.horizontal(info.defaultLayoutStyle().spacing(10).alignment(ElementAlignment.ALIGN_CENTER))
.item(sieve.getMesh(), info.defaultItemStyle().width(16).height(16)) .item(mesh, info.defaultItemStyle().width(16).height(16))
.text(CompoundText.create().info(sieve.getMesh().getDescriptionId())); .text(CompoundText.create().info(mesh.getDescriptionId()));
if (mesh.isEnchanted()) { if (mesh.isEnchanted()) {
var list = new ObjectArrayList<Component>(); var list = new ObjectArrayList<Component>();
var style = info.defaultTextStyle().height(10); var style = info.defaultTextStyle().height(10);

View File

@ -133,6 +133,8 @@ public class EConfig {
public final BooleanValue allowWitchWaterEntityConversion; public final BooleanValue allowWitchWaterEntityConversion;
public final IntValue mechanicalSieveEnergyStorage; public final IntValue mechanicalSieveEnergyStorage;
public final IntValue mechanicalSieveEnergyConsumption; public final IntValue mechanicalSieveEnergyConsumption;
public final IntValue mechanicalHammerEnergyStorage;
public final IntValue mechanicalHammerEnergyConsumption;
public Server(ForgeConfigSpec.Builder builder) { public Server(ForgeConfigSpec.Builder builder) {
builder.comment("Server configuration for Ex Deorum").push("server"); builder.comment("Server configuration for Ex Deorum").push("server");
@ -179,6 +181,12 @@ public class EConfig {
this.mechanicalSieveEnergyConsumption = builder this.mechanicalSieveEnergyConsumption = builder
.comment("The amount of FE/t a tick consumed by the mechanical sieve when sifting a block.") .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); .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(); builder.pop();
} }

View File

@ -65,6 +65,11 @@ class BlockLoot extends BlockLootSubProvider {
.setRolls(ConstantValue.exactly(1.0F)) .setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(EItems.MECHANICAL_SIEVE.get()) .add(LootItem.lootTableItem(EItems.MECHANICAL_SIEVE.get())
.apply(MachineLootFunction.machineLoot()))))); .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 @Override

View File

@ -31,6 +31,7 @@ class English {
english.add(TranslationKeys.VOID_WORLD_TYPE, "Void World"); english.add(TranslationKeys.VOID_WORLD_TYPE, "Void World");
english.add(TranslationKeys.FRACTION_DISPLAY, ": %s / %s"); english.add(TranslationKeys.FRACTION_DISPLAY, ": %s / %s");
english.add(TranslationKeys.MECHANICAL_SIEVE_MESH_LABEL, "Mesh: "); english.add(TranslationKeys.MECHANICAL_SIEVE_MESH_LABEL, "Mesh: ");
english.add(TranslationKeys.MECHANICAL_HAMMER_HAMMER_LABEL, "Hammer: ");
english.add(TranslationKeys.ENERGY, "Energy"); english.add(TranslationKeys.ENERGY, "Energy");
english.add(TranslationKeys.ROOT_ADVANCEMENT_TITLE, "Don't Look Down..."); 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.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.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_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_CATEGORY_TITLE, "Barrel Compost");
english.add(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, "Compost: %s"); 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_MODES[RedstoneControlWidget.REDSTONE_MODE_POWERED], "Powered");
english.add(TranslationKeys.REDSTONE_CONTROL_LABEL, "Redstone Mode"); english.add(TranslationKeys.REDSTONE_CONTROL_LABEL, "Redstone Mode");
english.add(TranslationKeys.REDSTONE_CONTROL_MODE, "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.VEXING_ARCHWOOD_CRUCIBLE, "Vexing Archwood Crucible");
english.addBlock(EBlocks.CASCADING_ARCHWOOD_CRUCIBLE, "Cascading Archwood Crucible"); english.addBlock(EBlocks.CASCADING_ARCHWOOD_CRUCIBLE, "Cascading Archwood Crucible");

View File

@ -27,6 +27,7 @@ public class TranslationKeys {
// ": %s / %s" // ": %s / %s"
public static final String FRACTION_DISPLAY = "item." + ExDeorum.ID + ".watering_can_fluid_display"; 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_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"; public static final String ENERGY = "gui." + ExDeorum.ID + ".energy_label";
// Advancements // 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 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 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_SIEVE_JEI_INFO = "info." + ExDeorum.ID + ".mechanical_sieve";
public static final String MECHANICAL_HAMMER_JEI_INFO = "info." + ExDeorum.ID + ".mechanical_hammer";
// JEI recipe categories // JEI recipe categories
public static final String BARREL_COMPOST_CATEGORY_TITLE = "gui." + ExDeorum.ID + ".category.barrel_compost"; 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_LABEL = "gui." + ExDeorum.ID + ".redstone_control.label";
public static final String REDSTONE_CONTROL_MODE = "gui." + ExDeorum.ID + ".redstone_control.mode"; 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";
} }

View File

@ -293,6 +293,16 @@ public class Recipes {
recipe.pattern("I I"); recipe.pattern("I I");
MKRecipeProvider.unlockedByHaving(recipe, Items.HOPPER); 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<? extends Item> sides, RegistryObject<? extends Item> middle, RegistryObject<? extends Item> result) { private static void modUShaped(MKRecipeProvider recipes, String modid, RegistryObject<? extends Item> sides, RegistryObject<? extends Item> middle, RegistryObject<? extends Item> result) {

View File

@ -21,6 +21,7 @@ package thedarkcolour.exdeorum.loot;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.Enchantments; 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 // fortune handling; more likely to boost drops if there are none to begin with
if (context.hasParam(LootContextParams.TOOL)) { if (context.hasParam(LootContextParams.TOOL)) {
var stack = context.getParam(LootContextParams.TOOL); var hammer = context.getParam(LootContextParams.TOOL);
var fortune = stack.getEnchantmentLevel(Enchantments.BLOCK_FORTUNE); resultAmount += calculateFortuneBonus(hammer, context.getRandom(), resultAmount == 0);
if (fortune != 0) {
var chance = context.getRandom().nextFloat();
if (resultAmount == 0) {
if (chance < 0.06 * fortune) {
resultAmount++;
}
} else {
if (chance < 0.03 * fortune) {
resultAmount++;
}
}
}
} }
if (resultAmount > 0) { if (resultAmount > 0) {
@ -89,5 +76,32 @@ public class HammerLootModifier extends LootModifier {
public Codec<? extends IGlobalLootModifier> codec() { public Codec<? extends IGlobalLootModifier> codec() {
return 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;
}
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<M extends AbstractMachineBlockEntity<M>> 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 extends AbstractMachineBlockEntity<M>> 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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}

View File

@ -20,38 +20,24 @@ package thedarkcolour.exdeorum.menu;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity;
import thedarkcolour.exdeorum.network.NetworkHandler;
import thedarkcolour.exdeorum.registry.EMenus; import thedarkcolour.exdeorum.registry.EMenus;
public class MechanicalSieveMenu extends EContainerMenu { public class MechanicalSieveMenu extends AbstractMachineMenu<MechanicalSieveBlockEntity> {
private static final ResourceLocation EMPTY_SLOT_MESH = new ResourceLocation(ExDeorum.ID, "item/empty_slot_mesh"); 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 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) { public MechanicalSieveMenu(int containerId, Inventory playerInventory, FriendlyByteBuf data) {
this(containerId, playerInventory, (MechanicalSieveBlockEntity) playerInventory.player.level().getBlockEntity(data.readBlockPos())); this(containerId, playerInventory, (MechanicalSieveBlockEntity) readPayload(playerInventory, data));
this.sieve.setRedstoneMode(data.readByte());
} }
public MechanicalSieveMenu(int containerId, Inventory playerInventory, MechanicalSieveBlockEntity sieve) { public MechanicalSieveMenu(int containerId, Inventory playerInventory, MechanicalSieveBlockEntity sieve) {
super(EMenus.MECHANICAL_SIEVE.get(), containerId); super(EMenus.MECHANICAL_SIEVE.get(), containerId, playerInventory, sieve);
this.sieve = sieve;
// input slot // input slot
addSlot(sieve.inventory.createSlot(0, 26, 30)); 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)); addSlot(sieve.inventory.createSlot(2 + r * 5 + c, 80 + c * 18, 15 + r * 18));
} }
} }
// Player slots addPlayerSlots(playerInventory, 91);
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;
}
} }
@Override @Override
@ -130,11 +69,11 @@ public class MechanicalSieveMenu extends EContainerMenu {
if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) { if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) {
return ItemStack.EMPTY; 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)) { if (!moveItemStackTo(clickedStack, 0, 1, false)) {
return ItemStack.EMPTY; 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)) { if (!moveItemStackTo(clickedStack, 1, 2, false)) {
return ItemStack.EMPTY; return ItemStack.EMPTY;
} }
@ -161,18 +100,4 @@ public class MechanicalSieveMenu extends EContainerMenu {
return stack; 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);
}
} }

View File

@ -23,7 +23,7 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import thedarkcolour.exdeorum.blockentity.EBlockEntity; import thedarkcolour.exdeorum.blockentity.EBlockEntity;
import thedarkcolour.exdeorum.client.ClientHandler; import thedarkcolour.exdeorum.client.ClientHandler;
import thedarkcolour.exdeorum.menu.EContainerMenu; import thedarkcolour.exdeorum.menu.AbstractMachineMenu;
public class ClientMessageHandler { public class ClientMessageHandler {
public static boolean isInVoidWorld; public static boolean isInVoidWorld;
@ -54,7 +54,7 @@ public class ClientMessageHandler {
public static void handleMenuProperty(MenuPropertyMessage msg) { public static void handleMenuProperty(MenuPropertyMessage msg) {
Player player = Minecraft.getInstance().player; 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()); menu.setClientProperty(msg.index(), msg.value());
} }
} }

View File

@ -24,6 +24,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper; import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container; 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.item.crafting.RecipeType;
import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids; 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.LootDataType;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.providers.number.*; import net.minecraft.world.level.storage.loot.providers.number.*;
import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
@ -55,6 +58,7 @@ import thedarkcolour.exdeorum.registry.ERecipeTypes;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
public final class RecipeUtil { public final class RecipeUtil {
@ -298,7 +302,12 @@ public final class RecipeUtil {
} }
} }
@SuppressWarnings("deprecation")
public static boolean isTagEmpty(TagKey<Item> tag) { public static boolean isTagEmpty(TagKey<Item> tag) {
return BuiltInRegistries.ITEM.getTag(tag).map(set -> !set.iterator().hasNext()).orElse(PreferredOres.getPreferredOre(tag) == Items.AIR); 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);
}
} }

View File

@ -87,6 +87,7 @@ public class SieveRecipe extends ProbabilityRecipe {
return new SieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly); return new SieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly);
} }
@SuppressWarnings("deprecation")
@Override @Override
public @Nullable SieveRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { public @Nullable SieveRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
Ingredient ingredient = Ingredient.fromNetwork(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()); return new SieveRecipe(id, ingredient, mesh, result, resultAmount, buffer.readBoolean());
} }
@SuppressWarnings("deprecation")
@Override @Override
public void toNetwork(FriendlyByteBuf buffer, SieveRecipe recipe) { public void toNetwork(FriendlyByteBuf buffer, SieveRecipe recipe) {
recipe.getIngredient().toNetwork(buffer); recipe.getIngredient().toNetwork(buffer);

View File

@ -26,6 +26,7 @@ import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity; import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity;
import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity;
import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity;
import thedarkcolour.exdeorum.blockentity.MechanicalHammerBlockEntity;
import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity;
import thedarkcolour.exdeorum.blockentity.SieveBlockEntity; import thedarkcolour.exdeorum.blockentity.SieveBlockEntity;
import thedarkcolour.exdeorum.blockentity.WaterCrucibleBlockEntity; import thedarkcolour.exdeorum.blockentity.WaterCrucibleBlockEntity;
@ -156,4 +157,5 @@ public class EBlockEntities {
EBlocks.CRYSTALLIZED_SIEVE.get() EBlocks.CRYSTALLIZED_SIEVE.get()
).build(null)); ).build(null));
public static final RegistryObject<BlockEntityType<MechanicalSieveBlockEntity>> MECHANICAL_SIEVE = BLOCK_ENTITIES.register("mechanical_sieve", () -> BlockEntityType.Builder.of(MechanicalSieveBlockEntity::new, EBlocks.MECHANICAL_SIEVE.get()).build(null)); public static final RegistryObject<BlockEntityType<MechanicalSieveBlockEntity>> MECHANICAL_SIEVE = BLOCK_ENTITIES.register("mechanical_sieve", () -> BlockEntityType.Builder.of(MechanicalSieveBlockEntity::new, EBlocks.MECHANICAL_SIEVE.get()).build(null));
public static final RegistryObject<BlockEntityType<MechanicalHammerBlockEntity>> MECHANICAL_HAMMER = BLOCK_ENTITIES.register("mechanical_hammer", () -> BlockEntityType.Builder.of(MechanicalHammerBlockEntity::new, EBlocks.MECHANICAL_HAMMER.get()).build(null));
} }

View File

@ -121,6 +121,8 @@ public class EBlocks {
public static final RegistryObject<SieveBlock> CRYSTALLIZED_SIEVE = registerSieve("crystallized_sieve", SoundType.GLASS); public static final RegistryObject<SieveBlock> CRYSTALLIZED_SIEVE = registerSieve("crystallized_sieve", SoundType.GLASS);
// Mechanical Sieve // Mechanical Sieve
public static final RegistryObject<MechanicalSieveBlock> MECHANICAL_SIEVE = BLOCKS.register("mechanical_sieve", () -> new MechanicalSieveBlock(of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().strength(5f, 1200f))); public static final RegistryObject<MechanicalSieveBlock> MECHANICAL_SIEVE = BLOCKS.register("mechanical_sieve", () -> new MechanicalSieveBlock(of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().strength(5f, 1200f)));
// Mechanical Hammer
public static final RegistryObject<MechanicalHammerBlock> MECHANICAL_HAMMER = BLOCKS.register("mechanical_hammer", () -> new MechanicalHammerBlock(of()));
// Lava Crucibles // Lava Crucibles
public static final RegistryObject<LavaCrucibleBlock> PORCELAIN_CRUCIBLE = registerLavaCrucible("porcelain_crucible", true, SoundType.STONE); public static final RegistryObject<LavaCrucibleBlock> PORCELAIN_CRUCIBLE = registerLavaCrucible("porcelain_crucible", true, SoundType.STONE);

View File

@ -225,6 +225,7 @@ public class EItems {
public static final RegistryObject<BlockItem> CRYSTALLIZED_SIEVE = registerItemBlock(EBlocks.CRYSTALLIZED_SIEVE); public static final RegistryObject<BlockItem> CRYSTALLIZED_SIEVE = registerItemBlock(EBlocks.CRYSTALLIZED_SIEVE);
// Mechanical Sieves // Mechanical Sieves
public static final RegistryObject<BlockItem> MECHANICAL_SIEVE = registerItemBlock(EBlocks.MECHANICAL_SIEVE); public static final RegistryObject<BlockItem> MECHANICAL_SIEVE = registerItemBlock(EBlocks.MECHANICAL_SIEVE);
public static final RegistryObject<BlockItem> MECHANICAL_HAMMER = registerItemBlock(EBlocks.MECHANICAL_HAMMER);
// Lava Crucibles // Lava Crucibles
public static final RegistryObject<BlockItem> PORCELAIN_CRUCIBLE = registerItemBlock(EBlocks.PORCELAIN_CRUCIBLE); public static final RegistryObject<BlockItem> PORCELAIN_CRUCIBLE = registerItemBlock(EBlocks.PORCELAIN_CRUCIBLE);
@ -365,6 +366,7 @@ public class EItems {
output.accept(CRYSTALLIZED_SIEVE.get()); output.accept(CRYSTALLIZED_SIEVE.get());
} }
output.accept(MECHANICAL_SIEVE.get()); output.accept(MECHANICAL_SIEVE.get());
output.accept(MECHANICAL_HAMMER.get());
output.accept(PORCELAIN_CRUCIBLE.get()); output.accept(PORCELAIN_CRUCIBLE.get());
output.accept(WARPED_CRUCIBLE.get()); output.accept(WARPED_CRUCIBLE.get());

View File

@ -25,10 +25,12 @@ import net.minecraftforge.network.IContainerFactory;
import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject; import net.minecraftforge.registries.RegistryObject;
import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.MechanicalHammerMenu;
import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; import thedarkcolour.exdeorum.menu.MechanicalSieveMenu;
public class EMenus { public class EMenus {
public static final DeferredRegister<MenuType<?>> MENUS = DeferredRegister.create(Registries.MENU, ExDeorum.ID); public static final DeferredRegister<MenuType<?>> MENUS = DeferredRegister.create(Registries.MENU, ExDeorum.ID);
public static final RegistryObject<MenuType<MechanicalSieveMenu>> MECHANICAL_SIEVE = MENUS.register("mechanical_sieve", () -> new MenuType<>((IContainerFactory<MechanicalSieveMenu>) MechanicalSieveMenu::new, FeatureFlags.DEFAULT_FLAGS)); public static final RegistryObject<MenuType<MechanicalSieveMenu>> MECHANICAL_SIEVE = MENUS.register("mechanical_sieve", () -> new MenuType<>((IContainerFactory<MechanicalSieveMenu>) MechanicalSieveMenu::new, FeatureFlags.DEFAULT_FLAGS));
public static final RegistryObject<MenuType<MechanicalHammerMenu>> MECHANICAL_HAMMER = MENUS.register("mechanical_hammer", () -> new MenuType<>((IContainerFactory<MechanicalHammerMenu>) MechanicalHammerMenu::new, FeatureFlags.DEFAULT_FLAGS));
} }

View File

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

View File

@ -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"
}
}

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB