diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index 25ca46f..33cc7e8 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,6 +1,7 @@ -// 1.21.1 2026-01-17T20:45:45.6230298 Loot Tables +// 1.21.1 2026-01-18T19:51:57.3558369 Loot Tables 69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json 05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json 1ab50c99e9f478840b9d003fd56ebdcab12fbbce data/aphelion/loot_table/blocks/test_block.json +7d8eeb99a1bc942a6e2cf292b21fd4534062b5ab data/aphelion/loot_table/blocks/vacuum_arc_furnace_controller.json diff --git a/src/generated/resources/.cache/a8139181fab9cfd94e67697230cbaca839a05e1f b/src/generated/resources/.cache/a8139181fab9cfd94e67697230cbaca839a05e1f index 34d1937..5980f6c 100644 --- a/src/generated/resources/.cache/a8139181fab9cfd94e67697230cbaca839a05e1f +++ b/src/generated/resources/.cache/a8139181fab9cfd94e67697230cbaca839a05e1f @@ -1,12 +1,15 @@ -// 1.21.1 2026-01-17T21:22:18.4335623 Block States: aphelion +// 1.21.1 2026-01-18T19:51:57.3548439 Block States: aphelion 851ff42f7b21dec86107c8e0cefb3934ae4ebc08 assets/aphelion/blockstates/block_steel.json 30b9c0efd7aaadb5412d98e4568f98b3632adbb9 assets/aphelion/blockstates/dimension_changer.json +cb4287104006c80c8396b290ab5258df65d62cef assets/aphelion/blockstates/electric_arc_furnace.json b86c50fddcf6c8c6c19cb748529239d5962a3ede assets/aphelion/blockstates/test_block.json a810b97f4dace35d026f28d96cb9c47c93600d75 assets/aphelion/models/block/block_steel.json 2d3592b7ab7132908709243e97540151e0fb762e assets/aphelion/models/block/dimension_changer.json +5f7e8674070f31a63875b5d6147153bfa0eef61a assets/aphelion/models/block/electric_arc_furnace.json e0971228b4a1c4bc9dbab58a7dacdc3ae6037e02 assets/aphelion/models/block/test_block.json cdc831b0f1c462be64825fd34bd446e5b95afac6 assets/aphelion/models/item/arc_furnace_casing.json 3599f9037eb2f66de1765318b97ab564c3eae92f assets/aphelion/models/item/block_steel.json db0ec473a016ce05c258cde18a217d47a9ea8324 assets/aphelion/models/item/dimension_changer.json 279080c06ada87f54fd0a7b885b256dbe25a946a assets/aphelion/models/item/electric_arc_furnace.json 74418ef1cf678e72e7534924274688ef5a68af0e assets/aphelion/models/item/test_block.json +88ca3602517e99f7feaed57eddfc96965a25761c assets/aphelion/models/item/vacuum_arc_furnace_controller.json diff --git a/src/main/java/net/xevianlight/aphelion/Aphelion.java b/src/main/java/net/xevianlight/aphelion/Aphelion.java index f4c5656..a942b09 100644 --- a/src/main/java/net/xevianlight/aphelion/Aphelion.java +++ b/src/main/java/net/xevianlight/aphelion/Aphelion.java @@ -15,6 +15,7 @@ import net.xevianlight.aphelion.recipe.ModRecipes; import net.xevianlight.aphelion.screen.ElectricArcFurnaceScreen; import net.xevianlight.aphelion.screen.ModMenuTypes; import net.xevianlight.aphelion.screen.TestBlockScreen; +import net.xevianlight.aphelion.screen.VacuumArcFurnaceScreen; import org.slf4j.Logger; import com.mojang.logging.LogUtils; @@ -120,6 +121,7 @@ public class Aphelion { public static void registerScreens(RegisterMenuScreensEvent event) { event.register(ModMenuTypes.TEST_BLOCK_MENU.get(), TestBlockScreen::new); event.register(ModMenuTypes.ELECTRIC_ARC_FURNACE_MENU.get(), ElectricArcFurnaceScreen::new); + event.register(ModMenuTypes.VACUUM_ARC_FURNACE_MENU.get(), VacuumArcFurnaceScreen::new); } } } diff --git a/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java b/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java index 9207afc..6400cc7 100644 --- a/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java +++ b/src/main/java/net/xevianlight/aphelion/block/custom/ArcFurnaceCasingBlock.java @@ -2,6 +2,11 @@ package net.xevianlight.aphelion.block.custom; import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; @@ -13,13 +18,17 @@ import net.minecraft.world.level.block.entity.BlockEntity; 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.phys.BlockHitResult; import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; +import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; +import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.MultiblockHelper; import org.jetbrains.annotations.Nullable; public class ArcFurnaceCasingBlock extends BaseEntityBlock { - public static final BooleanProperty FORMED = BooleanProperty.create("formed"); + public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED; public ArcFurnaceCasingBlock(Properties properties) { super(properties); @@ -27,6 +36,11 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { .setValue(FORMED, false)); } + @Override + protected MapCodec codec() { + return CODEC; + } + public static Properties getProperties() { return Properties .of() @@ -42,18 +56,18 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { public static final MapCodec CODEC = simpleCodec(ArcFurnaceCasingBlock::new); - @Override - protected MapCodec codec() { - return CODEC; - } +// @Override +// protected MapCodec codec() { +// return CODEC; +// } - @Override - public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - return new EAFPartEntity(blockPos, blockState); - } +// @Override +// public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { +// return new EAFPartEntity(blockPos, blockState); +// } private void pingNearbyController(Level level, BlockPos pos) { - int r = 3; + int r = 5; BlockPos.MutableBlockPos mp = new BlockPos.MutableBlockPos(); for (int dx=-r; dx<=r; dx++) @@ -61,14 +75,28 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { for (int dz=-r; dz<=r; dz++) { mp.set(pos.getX()+dx, pos.getY()+dy, pos.getZ()+dz); BlockEntity be = level.getBlockEntity(mp); - if (be instanceof ElectricArcFurnaceEntity eaf) { - if (level.getBlockState(eaf.getBlockPos()).getBlock() instanceof ElectricArcFurnace) { - eaf.tryForm(); + if (be instanceof VacuumArcFurnaceControllerEntity vaf) { + if (level.getBlockState(vaf.getBlockPos()).getBlock() instanceof VacuumArcFurnaceController) { + MultiblockHelper.tryForm(level, vaf.getBlockState(), vaf.getBlockPos(), VacuumArcFurnaceControllerEntity.SHAPE, AphelionBlockStateProperties.FORMED); } } } } + @Override + public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { + if (state.getValue(AphelionBlockStateProperties.FORMED)) { + if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof EAFPartEntity eafPartEntity) { + if (eafPartEntity.getControllerPos() != null) + if (level.getBlockEntity(eafPartEntity.getControllerPos()) instanceof VacuumArcFurnaceControllerEntity) + serverPlayer.openMenu(new SimpleMenuProvider((VacuumArcFurnaceControllerEntity) level.getBlockEntity(eafPartEntity.getControllerPos()), Component.literal("Vacuum Arc Furnace")), eafPartEntity.getControllerPos()); + } + return InteractionResult.sidedSuccess(level.isClientSide); + } + + return InteractionResult.FAIL; + } + @Override public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { super.onPlace(state, level, pos, oldState, movedByPiston); @@ -97,4 +125,9 @@ public class ArcFurnaceCasingBlock extends BaseEntityBlock { protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(FORMED); } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new EAFPartEntity(blockPos, blockState); + } } diff --git a/src/main/java/net/xevianlight/aphelion/block/custom/ElectricArcFurnace.java b/src/main/java/net/xevianlight/aphelion/block/custom/ElectricArcFurnace.java index 9ce16cf..01fceae 100644 --- a/src/main/java/net/xevianlight/aphelion/block/custom/ElectricArcFurnace.java +++ b/src/main/java/net/xevianlight/aphelion/block/custom/ElectricArcFurnace.java @@ -27,6 +27,8 @@ import net.minecraft.world.phys.BlockHitResult; import net.neoforged.neoforge.items.ItemStackHandler; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.MultiblockHelper; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -36,7 +38,7 @@ public class ElectricArcFurnace extends BaseEntityBlock { public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public static final BooleanProperty LIT = BlockStateProperties.LIT; - public static final BooleanProperty FORMED = BooleanProperty.create("formed"); + public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED; public ElectricArcFurnace(Properties properties) { super(properties); @@ -72,8 +74,8 @@ public class ElectricArcFurnace extends BaseEntityBlock { @Override public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { - if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity && state.getValue(FORMED)) { - serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos); + if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) { + serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos); } return InteractionResult.sidedSuccess(level.isClientSide); } @@ -95,7 +97,7 @@ public class ElectricArcFurnace extends BaseEntityBlock { if (!level.isClientSide && state.getBlock() != newState.getBlock()) { BlockEntity blockEntity= level.getBlockEntity(pos); if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) { - if(state.getValue(FORMED)) electricArcFurnaceEntity.unformForRemoval(); +// if(state.getValue(FORMED)) MultiblockHelper.unformForRemoval(level, state, pos, ElectricArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED); electricArcFurnaceEntity.drops(); } } @@ -107,9 +109,9 @@ public class ElectricArcFurnace extends BaseEntityBlock { super.onPlace(state, level, pos, oldState, movedByPiston); if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) { BlockEntity blockEntity= level.getBlockEntity(pos); - if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) { - electricArcFurnaceEntity.tryForm(); - } +// if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) { +// MultiblockHelper.tryForm(level, state, pos, ElectricArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED); +// } } } diff --git a/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java b/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java new file mode 100644 index 0000000..d036da0 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/custom/VacuumArcFurnaceController.java @@ -0,0 +1,196 @@ +package net.xevianlight.aphelion.block.custom; + +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +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.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; +import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.MultiblockHelper; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class VacuumArcFurnaceController extends BaseEntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + public static final BooleanProperty LIT = BlockStateProperties.LIT; + public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED; + + public VacuumArcFurnaceController(Properties properties) { + super(properties); + this.registerDefaultState(this.getStateDefinition().any() + .setValue(FORMED, false)); + } + + public static final MapCodec CODEC = simpleCodec(VacuumArcFurnaceController::new); + + private final int INPUT_SLOT = 0; + private final int SECONDARY_INPUT_SLOT = 1; + private final int OUTPUT_SLOT = 2; + private final int ENERGY_SLOT = 3; + + @Override + protected MapCodec codec() { + return CODEC; + } + + public static Properties getProperties() { + return Properties + .of() + .sound(SoundType.METAL) + .destroyTime(2f) + .explosionResistance(10f) + .requiresCorrectToolForDrops(); + } + + public static Item.Properties getItemProperties() { + return new Item.Properties(); + } + + @Override + public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { + + if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) { + if (vacuumArcFurnaceEntity.isFormed()) + serverPlayer.openMenu(new SimpleMenuProvider(vacuumArcFurnaceEntity, Component.literal("Vacuum Arc Furnace")), pos); + } + return InteractionResult.sidedSuccess(level.isClientSide); + } + + + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new VacuumArcFurnaceControllerEntity(blockPos, blockState); + } + + @Override + public RenderShape getRenderShape(BlockState pState) { + return RenderShape.MODEL; + } + + @Override + protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!level.isClientSide && state.getBlock() != newState.getBlock()) { + BlockEntity blockEntity= level.getBlockEntity(pos); + if (blockEntity instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) { + if(state.getValue(FORMED)) MultiblockHelper.unformForRemoval(level, state, pos, vacuumArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED); + vacuumArcFurnaceEntity.drops(); + } + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + super.onPlace(state, level, pos, oldState, movedByPiston); + if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) { + BlockEntity blockEntity= level.getBlockEntity(pos); + if (blockEntity instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) { + MultiblockHelper.tryForm(level, state, pos, vacuumArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED); + } + } + } + + public static int getRedstoneSignalFromItemHandler(@javax.annotation.Nullable ItemStackHandler itemStackHandler, List slots) { + if (itemStackHandler == null) { + return 0; + } else { + int i = 0; + float f = 0.0F; + + for(int slot : slots) { + ItemStack itemstack = itemStackHandler.getStackInSlot(slot); + if (!itemstack.isEmpty()) { + f += (float)itemstack.getCount() / (float)Math.min(itemStackHandler.getSlotLimit(slot), itemstack.getMaxStackSize()); + ++i; + } + } + + f /= (float)slots.size(); + return Mth.floor(f * 14.0F) + (i > 0 ? 1 : 0); + } + } + + @Override + protected boolean isSignalSource(BlockState state) { + return true; + } + + @Override + protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + return super.getSignal(state, level, pos, direction); + } + + @Override + protected boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) { + List slots = new ArrayList<>(); + slots.add(VacuumArcFurnaceControllerEntity.INPUT_SLOT); + slots.add(VacuumArcFurnaceControllerEntity.SECONDARY_INPUT_SLOT); + slots.add(VacuumArcFurnaceControllerEntity.OUTPUT_SLOT); + VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity = ((VacuumArcFurnaceControllerEntity) level.getBlockEntity(pos)); + + if (vacuumArcFurnaceEntity != null) + return getRedstoneSignalFromItemHandler(vacuumArcFurnaceEntity.inventory, slots); + + return 0; + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { + if (level.isClientSide) { + return null; + } + return createTickerHelper(blockEntityType, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), (level1, blockPos, blockState, vacuumArcFurnaceEntity) -> vacuumArcFurnaceEntity.tick(level1, blockPos, blockState)); + + } + + @Override + protected BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + protected BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } + + @Override + public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING, LIT, FORMED); + } +} diff --git a/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java b/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java index 6b3041d..7243475 100644 --- a/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java +++ b/src/main/java/net/xevianlight/aphelion/block/entity/custom/EAFPartEntity.java @@ -7,9 +7,10 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.util.IMultiblockPart; import org.jetbrains.annotations.Nullable; -public class EAFPartEntity extends BlockEntity { +public class EAFPartEntity extends BlockEntity implements IMultiblockPart { @Nullable private BlockPos controllerPos; public EAFPartEntity(BlockPos pos, BlockState blockState) { diff --git a/src/main/java/net/xevianlight/aphelion/block/entity/custom/ElectricArcFurnaceEntity.java b/src/main/java/net/xevianlight/aphelion/block/entity/custom/ElectricArcFurnaceEntity.java index 9fa1943..532a3a7 100644 --- a/src/main/java/net/xevianlight/aphelion/block/entity/custom/ElectricArcFurnaceEntity.java +++ b/src/main/java/net/xevianlight/aphelion/block/entity/custom/ElectricArcFurnaceEntity.java @@ -8,12 +8,9 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Containers; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -23,26 +20,24 @@ import net.minecraft.world.item.crafting.*; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.energy.IEnergyStorage; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.ItemStackHandler; -import net.xevianlight.aphelion.block.custom.ArcFurnaceCasingBlock; import net.xevianlight.aphelion.block.custom.ElectricArcFurnace; import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage; import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.core.init.ModBlocks; import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe; import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipeInput; import net.xevianlight.aphelion.recipe.ModRecipes; import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu; +import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.MultiblockHelper; import net.xevianlight.aphelion.util.SidedSlotHandler; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider { @@ -108,7 +103,7 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide public void tick(Level level, BlockPos pos, BlockState blockState) { - if (!blockState.getValue(ElectricArcFurnace.FORMED)) + if (!blockState.getValue(AphelionBlockStateProperties.FORMED)) return; chargeFromItem(); @@ -316,6 +311,7 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide } private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT, ENERGY_SLOT}, true, true); + private final IItemHandler emptyJeiHandler = new SidedSlotHandler(inventory, new int[]{}, false, false); private final IItemHandler inputHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true); private final IItemHandler outputHandler = new SidedSlotHandler(inventory, new int[]{2,3}, false, true); private final IItemHandler jadeHandler = new SidedSlotHandler(inventory, new int[]{0}, false, false); @@ -325,13 +321,18 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide } public IEnergyStorage getEnergyStorage(@Nullable Direction direction) { + return ENERGY_STORAGE; + } + + public IEnergyStorage getTrueEnergyStorage(@Nullable Direction direction) { return this.ENERGY_STORAGE; } - private final ModEnergyStorage ENERGY_STORAGE = createEnergyStorage(); + private final ModEnergyStorage ENERGY_STORAGE = createEnergyStorage(ENERGY_CAPACITY, MAX_TRANSFER); + private final ModEnergyStorage NULL_ENERGY_STORAGE = createEnergyStorage(0, 0); - private ModEnergyStorage createEnergyStorage() { - return new ModEnergyStorage(ENERGY_CAPACITY, MAX_TRANSFER) { + private ModEnergyStorage createEnergyStorage(int capacity, int transfer) { + return new ModEnergyStorage(capacity, transfer) { @Override public void onEnergyChanged() { setChanged(); @@ -378,7 +379,7 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide @Override public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { - return new ElectricArcFurnaceMenu(i, inventory, this, this.data); + return new ElectricArcFurnaceMenu(i, inventory, this, this.data); } @Nullable @@ -403,132 +404,8 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide }; } - - - /// MULTIBLOCK - /// LOGIC - /// BELOW - - - - private boolean formed = false; // cached runtime state - - private static final BlockPos[] SHAPE = new BlockPos[] { - new BlockPos(1, 0, 0), - new BlockPos(0, 0, 1), - new BlockPos(1, 0, 1), - - new BlockPos(0, 1, 0), - new BlockPos(1, 1, 0), - new BlockPos(0, 1, 1), - new BlockPos(1, 1, 1), + public static final MultiblockHelper.ShapePart[] SHAPE = new MultiblockHelper.ShapePart[] { + new MultiblockHelper.ShapePart(new BlockPos(3,0,3), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), +// new MultiblockHelper.ShapePart(new BlockPos(-1,0,0), s -> s.is(Blocks.AMETHYST_BLOCK)) }; - - private List getPartPositions() { - Direction facing = getBlockState().getValue(BlockStateProperties.HORIZONTAL_FACING); - BlockPos origin = getBlockPos(); - - List out = new ArrayList<>(SHAPE.length); - for (BlockPos off : SHAPE) { - BlockPos ro = rotateY(off, facing); - out.add(origin.offset(ro)); - } - return out; - } - - private boolean structureMatches() { - if (level == null) return false; - - for (BlockPos p : getPartPositions()) { - BlockState st = level.getBlockState(p); - - // Accept only your casing/part blocks - if (!(st.getBlock() instanceof ArcFurnaceCasingBlock)) { - return false; - } - } - return true; - } - - public boolean isFormed() { - return formed; - } - - public void tryForm() { - if (level == null || level.isClientSide()) return; - if (!(getBlockState().getBlock() instanceof ElectricArcFurnace)) return; // don't form if not actually present - - boolean valid = structureMatches(); - - if (valid && !formed) { - formed = true; - setFormedState(true); - linkParts(); - } else if (!valid && formed) { - unform(); - } - } - - public void unform() { - if (level == null || level.isClientSide()) return; - - formed = false; - setFormedState(false); - unlinkParts(); - } - - public void unformForRemoval() { - if (level == null || level.isClientSide()) return; - - formed = false; - unlinkParts(); - } - - private void setFormedState(boolean value) { - BlockState state = getBlockState(); - if (state.hasProperty(ElectricArcFurnace.FORMED) && state.getValue(ElectricArcFurnace.FORMED) != value) { - level.setBlock(worldPosition, state.setValue(ElectricArcFurnace.FORMED, value), 3); - } - } - - private void linkParts() { - if (level == null || level.isClientSide()) return; - - for (BlockPos p : getPartPositions()) { - BlockState st = level.getBlockState(p); - - if (st.getBlock() instanceof ArcFurnaceCasingBlock && st.hasProperty(ArcFurnaceCasingBlock.FORMED)) { - if (!st.getValue(ArcFurnaceCasingBlock.FORMED)) { - level.setBlock(p, st.setValue(ArcFurnaceCasingBlock.FORMED, true), 3); - } - } - - BlockEntity be = level.getBlockEntity(p); - if (be instanceof EAFPartEntity part) { - part.setControllerPos(worldPosition); - part.setChanged(); - } - } - } - - private void unlinkParts() { - if (level == null || level.isClientSide()) return; - - for (BlockPos p : getPartPositions()) { - BlockState st = level.getBlockState(p); - - if (st.getBlock() instanceof ArcFurnaceCasingBlock && st.hasProperty(ArcFurnaceCasingBlock.FORMED)) { - if (st.getValue(ArcFurnaceCasingBlock.FORMED)) { - level.setBlock(p, st.setValue(ArcFurnaceCasingBlock.FORMED, false), 3); - } - } - - BlockEntity be = level.getBlockEntity(p); - if (be instanceof EAFPartEntity part && worldPosition.equals(part.getControllerPos())) { - part.setControllerPos(null); - part.setChanged(); - } - } - } - } diff --git a/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java b/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java new file mode 100644 index 0000000..dcb7a50 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/block/entity/custom/VacuumArcFurnaceControllerEntity.java @@ -0,0 +1,507 @@ +package net.xevianlight.aphelion.block.entity.custom; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.Containers; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleContainer; +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.ContainerData; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.BlastingRecipe; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.item.crafting.SingleRecipeInput; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.energy.IEnergyStorage; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.xevianlight.aphelion.block.custom.ElectricArcFurnace; +import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage; +import net.xevianlight.aphelion.core.init.ModBlockEntities; +import net.xevianlight.aphelion.core.init.ModBlocks; +import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe; +import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipeInput; +import net.xevianlight.aphelion.recipe.ModRecipes; +import net.xevianlight.aphelion.screen.VacuumArcFurnaceMenu; +import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu; +import net.xevianlight.aphelion.util.AphelionBlockStateProperties; +import net.xevianlight.aphelion.util.IMultiblockController; +import net.xevianlight.aphelion.util.MultiblockHelper; +import net.xevianlight.aphelion.util.SidedSlotHandler; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class VacuumArcFurnaceControllerEntity extends BlockEntity implements MenuProvider, IMultiblockController { + + private final int SIZE = 4; + private int ENERGY_CAPACITY = 64000; + private int MAX_TRANSFER = 320; + private int progress = 0; + private int maxProgress = 100; + private final int DEFAULT_MAX_PROGRESS = 100; + private final ContainerData data; + private int MACHINE_ENERGY_COST = 20; + + public static final int INPUT_SLOT = 0; + public static final int SECONDARY_INPUT_SLOT = 1; + public static final int OUTPUT_SLOT = 2; + public static final int ENERGY_SLOT = 3; + + public VacuumArcFurnaceControllerEntity(BlockPos pos, BlockState blockState) { + super(ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), pos, blockState); + this.data = new ContainerData() { + @Override + public int get(int index) { + return switch (index) { + case 0 -> VacuumArcFurnaceControllerEntity.this.progress; + case 1 -> VacuumArcFurnaceControllerEntity.this.maxProgress; + default -> 0; + }; + } + + @Override + public void set(int index, int pValue) { + switch (index) { + case 0: VacuumArcFurnaceControllerEntity.this.progress = pValue; + case 1: VacuumArcFurnaceControllerEntity.this.maxProgress = pValue; + } + } + + @Override + public int getCount() { + return 2; + } + }; + } + + public final ItemStackHandler inventory = new ItemStackHandler(SIZE) { + @Override + protected void onContentsChanged(int slot) { + setChanged(); + if(!level.isClientSide()) { + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3); + } + } + +// @Override +// public boolean isItemValid(int slot, ItemStack stack) { +// if (slot == ENERGY_SLOT) { +// var capability = stack.getCapability(Capabilities.EnergyStorage.ITEM); +// return capability != null; +// } +// return super.isItemValid(slot, stack); +// } + }; + + public void tick(Level level, BlockPos pos, BlockState blockState) { + + if (!blockState.getValue(AphelionBlockStateProperties.FORMED)) + return; + + chargeFromItem(); + + if (inventory.getStackInSlot(SECONDARY_INPUT_SLOT).isEmpty()) { + // Secondary slot is empty, try furnace recipes + if (hasFurnaceRecipe(INPUT_SLOT) && hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) { + // Recipe detected! We have enough energy to process + progress++; + useEnergy(); + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true)); + setChanged(level, pos, blockState); + + if (hasCraftingFinished()) { + outputBlastingResult(INPUT_SLOT, OUTPUT_SLOT); + resetProgress(); + } + } else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) { + // Recipe detected but we ran out of power + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, blockState); + progress = progress > 0 ? progress - 1 : 0; + } else { + // Invalid recipe + resetProgress(); + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, blockState); + } + } else { + // Secondary slot is NOT empty, try alloying recipes + if (hasAlloyingRecipe(INPUT_SLOT, SECONDARY_INPUT_SLOT)) { + if (hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) { + // Alloy recipe detected! We have enough energy to process + progress++; + useEnergy(); + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true)); + setChanged(level, pos, blockState); + + if (hasCraftingFinished()) { + outputAlloyingResult(INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT); + resetProgress(); + } + } else { + // Recipe detected but we ran out of power + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, blockState); + progress = progress > 0 ? progress - 1 : 0; + } + } else { + // Invalid recipe + resetProgress(); + level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false)); + setChanged(level, pos, blockState); + } + } + + + } + + private void chargeFromItem() { + ItemStack stack; + + try { + stack = inventory.getStackInSlot(ENERGY_SLOT); + + if (stack.isEmpty()) return; + + IEnergyStorage itemEnergy = stack.getCapability(Capabilities.EnergyStorage.ITEM); + if (itemEnergy == null || !itemEnergy.canExtract()) return; + + int freeCapacity = ENERGY_STORAGE.getMaxEnergyStored() - ENERGY_STORAGE.getEnergyStored(); + if (freeCapacity <= 0) return; + + int maxMove = Math.min(MAX_TRANSFER, freeCapacity); + + // Simulate extraction first + int canExtract = itemEnergy.extractEnergy(maxMove, true); + if (canExtract <= 0) return; + + // Receive into block (simulate then execute is safest) + int canReceive = ENERGY_STORAGE.receiveEnergy(canExtract, true); + if (canReceive <= 0) return; + + int extracted = itemEnergy.extractEnergy(canReceive, false); + ENERGY_STORAGE.receiveEnergy(maxMove, false); + + setChanged(); + } catch (Exception e) { + + } + } + + private void outputBlastingResult(int slot, int resultSlot) { + Optional> recipe = getFurnaceRecipe(inventory.getStackInSlot(slot)); + ItemStack output = recipe.get().value().getResultItem(null); + + // 2x multiplier for smelting recipes + + inventory.extractItem(slot, 1, false); + inventory.setStackInSlot(resultSlot, new ItemStack(output.getItem(), + inventory.getStackInSlot(resultSlot).getCount() + (output.getCount() * 2))); + } + + private void outputAlloyingResult(int inputSlot, int secondaryInputSlot, int outputSlot) { + Optional> recipe = getAlloyingRecipe(inventory.getStackInSlot(inputSlot), inventory.getStackInSlot(secondaryInputSlot)); + ItemStack output = recipe.get().value().getResultItem(null); + + inventory.extractItem(inputSlot, recipe.get().value().baseCount(), false); + inventory.extractItem(secondaryInputSlot, recipe.get().value().alloyCount(), false); + inventory.setStackInSlot(outputSlot, new ItemStack(output.getItem(), inventory.getStackInSlot(outputSlot).getCount() + (output.getCount()))); + } + + private void resetProgress() { + this.progress = 0; + this.maxProgress = DEFAULT_MAX_PROGRESS; + } + + private void useEnergy() { + this.ENERGY_STORAGE.extractEnergy(MACHINE_ENERGY_COST, false); + } + + private boolean hasEnoughEnergyToCraft(int energyCost) { + return ENERGY_STORAGE.getEnergyStored() >= energyCost; + } + + private boolean canInsertItemIntoOutputSlot(ItemStack output, int slot) { + return inventory.getStackInSlot(slot).isEmpty() || + inventory.getStackInSlot(slot).getItem() == output.getItem(); + } + + private boolean canInsertAmountIntoOutputSlot(int count, int slot) { + int maxCount = inventory.getStackInSlot(slot).isEmpty() ? 64 : inventory.getStackInSlot(slot).getMaxStackSize(); + int currentCount = inventory.getStackInSlot(slot).getCount(); + + return maxCount >= currentCount + count; + } + + private boolean isOutputSlotEmptyOrReceivable(int slot) { + return this.inventory.getStackInSlot(slot).isEmpty() || + this.inventory.getStackInSlot(slot).getCount() < this.inventory.getStackInSlot(slot).getMaxStackSize(); + } + + private boolean hasCraftingFinished() { + maxProgress = DEFAULT_MAX_PROGRESS; + return this.progress >= this.maxProgress; + } + + + private boolean hasAlloyingRecipe(int slotBase, int slotAlloy) { + ItemStack baseStack = inventory.getStackInSlot(slotBase); + ItemStack alloyStack = inventory.getStackInSlot(slotAlloy); + + Optional> opt = + getAlloyingRecipe(baseStack, alloyStack); + + if (opt.isEmpty()) return false; + + ElectricArcFurnaceRecipe recipe = opt.get().value(); + + // 1) Check required input counts + if (baseStack.getCount() < recipe.baseCount()) return false; + if (alloyStack.getCount() < recipe.alloyCount()) return false; + + // 2) Check output slot compatibility + space + ItemStack output = recipe.getResultItem(null); + + maxProgress = recipe.cookTime(); + + return canInsertItemIntoOutputSlot(output, OUTPUT_SLOT) + && canInsertAmountIntoOutputSlot(output.getCount(), OUTPUT_SLOT); + } + + private Optional> getAlloyingRecipe(ItemStack base, ItemStack alloy) { + if (base.isEmpty()) return Optional.empty(); + if (alloy.isEmpty()) return Optional.empty(); + + return this.level.getRecipeManager() + .getRecipeFor( + ModRecipes.ELECTRIC_ARC_FURNACE_RECIPE_TYPE.get(), + new ElectricArcFurnaceRecipeInput(base, alloy), + level + ); + } + + private boolean hasFurnaceRecipe(int slot) { + Optional> recipe = getFurnaceRecipe(new ItemStack(inventory.getStackInSlot(slot).getItem().asItem(), 1)); + if (recipe.isEmpty()) + return false; + + ItemStack output = recipe.get().value().getResultItem(null); + return canInsertAmountIntoOutputSlot(output.getCount() * 2, OUTPUT_SLOT) && canInsertItemIntoOutputSlot(output, OUTPUT_SLOT); + } + + private Optional> getFurnaceRecipe(ItemStack stack) { + if (stack.isEmpty()) return Optional.empty(); + + return this.level.getRecipeManager() + .getRecipeFor(RecipeType.BLASTING, new SingleRecipeInput(stack), level); + + } + + public void sendUpdate() { + setChanged(); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3); + } + + private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT, ENERGY_SLOT}, true, true); + private final IItemHandler emptyJeiHandler = new SidedSlotHandler(inventory, new int[]{}, false, false); + private final IItemHandler inputHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true); + private final IItemHandler outputHandler = new SidedSlotHandler(inventory, new int[]{2,3}, false, true); + private final IItemHandler jadeHandler = new SidedSlotHandler(inventory, new int[]{0}, false, false); + + public IItemHandler getItemHandler(Direction direction) { + if (direction != null) + return isFormed() ? fullHandler : null; + return isFormed() ? fullHandler : emptyJeiHandler; + } + + public IEnergyStorage getEnergyStorage(@Nullable Direction direction) { + if (direction == null) + return isFormed() ? ENERGY_STORAGE : NULL_ENERGY_STORAGE; + return isFormed() ? ENERGY_STORAGE : null; + } + + public IEnergyStorage getTrueEnergyStorage(@Nullable Direction direction) { + return this.ENERGY_STORAGE; + } + + private final ModEnergyStorage ENERGY_STORAGE = createEnergyStorage(ENERGY_CAPACITY, MAX_TRANSFER); + private final ModEnergyStorage NULL_ENERGY_STORAGE = createEnergyStorage(0, 0); + + private ModEnergyStorage createEnergyStorage(int capacity, int transfer) { + return new ModEnergyStorage(capacity, transfer) { + @Override + public void onEnergyChanged() { + setChanged(); + getLevel().sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3); + } + }; + } + + public ItemStackHandler getInventory() { + return inventory; + } + + public void drops() { + SimpleContainer inv = new SimpleContainer(inventory.getSlots()); + for(int i = 0; i < inventory.getSlots(); i++) { + inv.setItem(i, inventory.getStackInSlot(i)); + } + + Containers.dropContents(this.level, this.worldPosition, inv); + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider pRegistries) { + tag.put("inventory", inventory.serializeNBT(pRegistries)); + tag.putInt("electric_arc_furnace.progress", progress); + tag.putInt("electric_arc_furnace.maxProgress", maxProgress); + tag.putInt("electric_arc_furnace.energy", ENERGY_STORAGE.getEnergyStored()); + super.saveAdditional(tag, pRegistries); + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider pRegistries) { + super.loadAdditional(tag, pRegistries); + inventory.deserializeNBT(pRegistries, tag.getCompound("inventory")); + progress = tag.getInt("electric_arc_furnace.progress"); + maxProgress = tag.getInt("electric_arc_furnace.maxProgress"); + ENERGY_STORAGE.setEnergy(tag.getInt("electric_arc_furnace.energy")); + } + + @Override + public Component getDisplayName() { + return null; + } + + @Override + public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + if (isFormed()) + return new VacuumArcFurnaceMenu(i, inventory, this, this.data); + return new ElectricArcFurnaceMenu(i, inventory, this, this.data); + } + + @Nullable + @Override + public Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider pRegistries) { + return saveWithoutMetadata(pRegistries); + } + + private static BlockPos rotateY(BlockPos off, Direction facing) { + // Assumes "default" shape faces NORTH. + return switch (facing) { + case NORTH -> off; + case EAST -> new BlockPos(-off.getZ(), off.getY(), off.getX()); + case SOUTH -> new BlockPos(-off.getX(), off.getY(), -off.getZ()); + case WEST -> new BlockPos(off.getZ(), off.getY(), -off.getX()); + default -> off; + }; + } + + public static final MultiblockHelper.ShapePart[] SHAPE = new MultiblockHelper.ShapePart[] { + + //Layer -1 + new MultiblockHelper.ShapePart(new BlockPos(1,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,-1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,-1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,-1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + + //Layer 0 + new MultiblockHelper.ShapePart(new BlockPos(1,0,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,0,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,0,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,0,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,0,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,0,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,0,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,0,1), s -> s.is(Blocks.AIR)), + + //Layer 1 + + new MultiblockHelper.ShapePart(new BlockPos(1,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(1,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(-1,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,1,0), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,1,1), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), + new MultiblockHelper.ShapePart(new BlockPos(0,1,2), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)), +// new MultiblockHelper.ShapePart(new BlockPos(-1,0,0), s -> s.is(Blocks.AMETHYST_BLOCK)) + }; + + + + + /// MULTIBLOCK + /// LOGIC + /// BELOW + + +// record ShapePart (BlockPos offset, Predicate rule) {} + + private boolean formed = false; // cached runtime state + + // Assumes the default state is NORTH. + + // (Left -Right, Up -Down, Back -Front) + + +// private static final BlockPos[] SHAPE = new BlockPos[] { +// new BlockPos(1, 0, 0), +// new BlockPos(0, 0, 1), +// new BlockPos(1, 0, 1), +// +// new BlockPos(0, 1, 0), +// new BlockPos(1, 1, 0), +// new BlockPos(0, 1, 1), +// new BlockPos(1, 1, 1), +// }; + + + + public boolean isFormed() { + return getBlockState().hasProperty(AphelionBlockStateProperties.FORMED) + && getBlockState().getValue(AphelionBlockStateProperties.FORMED); + } + + @Override + public void setFormed(boolean formed) { + this.formed = formed; + invalidateCapabilities(); + if (!formed) { + drops(); + } + } + + private void setFormedState(boolean value) { + BlockState state = getBlockState(); + if (state.hasProperty(ElectricArcFurnace.FORMED) && state.getValue(ElectricArcFurnace.FORMED) != value) { + level.setBlock(worldPosition, state.setValue(ElectricArcFurnace.FORMED, value), 3); + } + } + +} diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java b/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java index 7997eb5..77df5c6 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModBlockEntities.java @@ -4,10 +4,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredRegister; import net.xevianlight.aphelion.Aphelion; -import net.xevianlight.aphelion.block.entity.custom.DimensionChangerBlockEntity; -import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity; -import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; -import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity; +import net.xevianlight.aphelion.block.entity.custom.*; import java.util.function.Supplier; @@ -33,4 +30,9 @@ public class ModBlockEntities { BLOCK_ENTITIES.register("eaf_part_entity", () -> BlockEntityType.Builder.of( EAFPartEntity::new, ModBlocks.ARC_FURNACE_CASING_BLOCK.get()).build(null) ); + + public static final Supplier> VACUUM_ARC_FURNACE_ENTITY = + BLOCK_ENTITIES.register("vacuum_arc_furnace_controller_entity", () -> BlockEntityType.Builder.of( + VacuumArcFurnaceControllerEntity::new, ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get()).build(null) + ); } diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java b/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java index 7fe732f..f8b30f4 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModBlocks.java @@ -14,4 +14,5 @@ public class ModBlocks { public static final DeferredBlock DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties())); public static final DeferredBlock ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties())); public static final DeferredBlock ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties())); + public static final DeferredBlock VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.getProperties())); } diff --git a/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java b/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java index 56ea81f..3753663 100644 --- a/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java +++ b/src/main/java/net/xevianlight/aphelion/core/init/ModItems.java @@ -33,4 +33,5 @@ public static final DeferredItem MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu public static final DeferredItem ELECTRIC_ARC_FURNACE = ITEMS.register("electric_arc_furnace", () -> new BlockItem(ModBlocks.ELECTRIC_ARC_FURNACE.get(), ElectricArcFurnace.getItemProperties())); public static final DeferredItem BLOCK_STEEL = ITEMS.register("block_steel", () -> new BlockItem(ModBlocks.BLOCK_STEEL.get(), BlockSteel.getItemProperties())); public static final DeferredItem ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties())); + public static final DeferredItem VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties())); } diff --git a/src/main/java/net/xevianlight/aphelion/datagen/ModBlockLootTableProvider.java b/src/main/java/net/xevianlight/aphelion/datagen/ModBlockLootTableProvider.java index d3c511e..bea13a3 100644 --- a/src/main/java/net/xevianlight/aphelion/datagen/ModBlockLootTableProvider.java +++ b/src/main/java/net/xevianlight/aphelion/datagen/ModBlockLootTableProvider.java @@ -21,6 +21,7 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider { dropSelf(ModBlocks.DIMENSION_CHANGER.get()); dropSelf(ModBlocks.ELECTRIC_ARC_FURNACE.get()); dropSelf(ModBlocks.ARC_FURNACE_CASING_BLOCK.get()); + dropSelf(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get()); } @Override diff --git a/src/main/java/net/xevianlight/aphelion/datagen/ModBlockStateProvider.java b/src/main/java/net/xevianlight/aphelion/datagen/ModBlockStateProvider.java index 8d547b3..e873abb 100644 --- a/src/main/java/net/xevianlight/aphelion/datagen/ModBlockStateProvider.java +++ b/src/main/java/net/xevianlight/aphelion/datagen/ModBlockStateProvider.java @@ -18,11 +18,12 @@ public class ModBlockStateProvider extends BlockStateProvider { protected void registerStatesAndModels() { blockWithItem(ModBlocks.TEST_BLOCK); -// horizontalBlock(ModBlocks.ELECTRIC_ARC_FURNACE.get(), models().orientable("aphelion:electric_arc_furnace", -// mcLoc("block/blast_furnace_side"), -// modLoc("block/electric_arc_furnace_front"), -// mcLoc("block/blast_furnace_top"))); + horizontalBlock(ModBlocks.ELECTRIC_ARC_FURNACE.get(), models().orientable("aphelion:electric_arc_furnace", + mcLoc("block/blast_furnace_side"), + modLoc("block/electric_arc_furnace_front"), + mcLoc("block/blast_furnace_top"))); blockItem(ModBlocks.ELECTRIC_ARC_FURNACE); + blockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER); blockWithItem(ModBlocks.BLOCK_STEEL); blockWithItem(ModBlocks.DIMENSION_CHANGER); diff --git a/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java b/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java index e49d036..41e9bfd 100644 --- a/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java +++ b/src/main/java/net/xevianlight/aphelion/event/ModBusEvents.java @@ -11,6 +11,7 @@ import net.neoforged.neoforge.network.registration.PayloadRegistrar; import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity; +import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; import net.xevianlight.aphelion.core.init.ModBlockEntities; import net.xevianlight.aphelion.network.ServerPayloadHandler; import net.xevianlight.aphelion.network.packet.PartitionData; @@ -22,6 +23,8 @@ public class ModBusEvents { event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.TEST_BLOCK_ENTITY.get(), TestBlockEntity::getItemHandler); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getItemHandler); event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage); + event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler); + event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage); } @SubscribeEvent diff --git a/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceMenu.java b/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceMenu.java index 287b142..7afa990 100644 --- a/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceMenu.java +++ b/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceMenu.java @@ -1,9 +1,6 @@ package net.xevianlight.aphelion.screen; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Container; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -14,7 +11,6 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.items.SlotItemHandler; -import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.util.EnergyItemSlot; import net.xevianlight.aphelion.util.ExtractOnlySlot; diff --git a/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceScreen.java b/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceScreen.java index 48192ba..3e58ab9 100644 --- a/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceScreen.java +++ b/src/main/java/net/xevianlight/aphelion/screen/ElectricArcFurnaceScreen.java @@ -62,8 +62,8 @@ public class ElectricArcFurnaceScreen extends AbstractContainerScreen,MenuType> TEST_BLOCK_MENU = registerMenuType("test_block_menu", TestBlockMenu::new); + public static DeferredHolder,MenuType> ELECTRIC_ARC_FURNACE_MENU = registerMenuType("electric_arc_furnace_menu", ElectricArcFurnaceMenu::new); + public static DeferredHolder,MenuType> VACUUM_ARC_FURNACE_MENU = + registerMenuType("vacuum_arc_furnace_menu", VacuumArcFurnaceMenu::new); + private static DeferredHolder, MenuType> registerMenuType(String name, IContainerFactory factory) { return MENUS.register(name, () -> IMenuTypeExtension.create(factory)); diff --git a/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceMenu.java b/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceMenu.java new file mode 100644 index 0000000..b8556f9 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceMenu.java @@ -0,0 +1,129 @@ +package net.xevianlight.aphelion.screen; + +import net.minecraft.network.FriendlyByteBuf; +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.ContainerData; +import net.minecraft.world.inventory.SimpleContainerData; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.items.SlotItemHandler; +import net.xevianlight.aphelion.block.custom.VacuumArcFurnaceController; +import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; +import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity; +import net.xevianlight.aphelion.util.EnergyItemSlot; +import net.xevianlight.aphelion.util.ExtractOnlySlot; +import org.jetbrains.annotations.NotNull; + +public class VacuumArcFurnaceMenu extends AbstractContainerMenu { + + public final VacuumArcFurnaceControllerEntity blockEntity; + private final Level level; + private final ContainerData data; + + public VacuumArcFurnaceMenu(int i, Inventory inventory, FriendlyByteBuf extraData) { + this(i, inventory, inventory.player.level().getBlockEntity(extraData.readBlockPos()), new SimpleContainerData(4)); + } + + public VacuumArcFurnaceMenu(int i, Inventory inventory, BlockEntity blockEntity, ContainerData data) { + super(ModMenuTypes.VACUUM_ARC_FURNACE_MENU.get(), i); + this.blockEntity = ((VacuumArcFurnaceControllerEntity) blockEntity); + this.level = inventory.player.level(); + this.data = data; + + addPlayerInventory(inventory); + addPlayerHotbar(inventory); + + this.addSlot(new EnergyItemSlot(this.blockEntity.inventory, VacuumArcFurnaceControllerEntity.ENERGY_SLOT, 8, 54)); + this.addSlot(new SlotItemHandler(this.blockEntity.inventory, VacuumArcFurnaceControllerEntity.INPUT_SLOT, 63, 35)); + this.addSlot(new SlotItemHandler(this.blockEntity.inventory, VacuumArcFurnaceControllerEntity.SECONDARY_INPUT_SLOT, 40, 35)); + this.addSlot(new ExtractOnlySlot(this.blockEntity.inventory, VacuumArcFurnaceControllerEntity.OUTPUT_SLOT, 125, 35)); + + addDataSlots(data); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + // CREDIT GOES TO: diesieben07 | https://github.com/diesieben07/SevenCommons + // must assign a slot number to each of the slots used by the GUI. + // For this container, we can see both the tile inventory's slots as well as the player inventory slots and the hotbar. + // Each time we add a Slot to the container, it automatically increases the slotIndex, which means + // 0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8) + // 9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35) + // 36 - 44 = TileInventory slots, which map to our TileEntity slot numbers 0 - 8) + private static final int HOTBAR_SLOT_COUNT = 9; + private static final int PLAYER_INVENTORY_ROW_COUNT = 3; + private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9; + private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT; + private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT; + private static final int VANILLA_FIRST_SLOT_INDEX = 0; + private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT; + + // THIS YOU HAVE TO DEFINE! + private static final int TE_INVENTORY_SLOT_COUNT = 3; // must be the number of slots you have! + @Override + public @NotNull ItemStack quickMoveStack(Player playerIn, int pIndex) { + Slot sourceSlot = slots.get(pIndex); + if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY; //EMPTY_ITEM + ItemStack sourceStack = sourceSlot.getItem(); + ItemStack copyOfSourceStack = sourceStack.copy(); + + // Check if the slot clicked is one of the vanilla container slots + if (pIndex < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) { + // This is a vanilla container slot so merge the stack into the tile inventory + if (!moveItemStackTo(sourceStack, TE_INVENTORY_FIRST_SLOT_INDEX, TE_INVENTORY_FIRST_SLOT_INDEX + + TE_INVENTORY_SLOT_COUNT, false)) { + blockEntity.sendUpdate(); + return ItemStack.EMPTY; // EMPTY_ITEM + } + } else if (pIndex < TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT) { + // This is a TE slot so merge the stack into the players inventory + if (!moveItemStackTo(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) { + blockEntity.sendUpdate(); + return ItemStack.EMPTY; + } + } else { + System.out.println("Invalid slotIndex:" + pIndex); + return ItemStack.EMPTY; + } + // If stack size == 0 (the entire stack was moved) set slot contents to null + if (sourceStack.getCount() == 0) { + sourceSlot.set(ItemStack.EMPTY); + blockEntity.sendUpdate(); + } else { + blockEntity.sendUpdate(); + sourceSlot.setChanged(); + } + sourceSlot.onTake(playerIn, sourceStack); + blockEntity.sendUpdate(); + return copyOfSourceStack; + } + + public int getScaledArrowProgress() { + int progress = this.data.get(0); + int maxProgress = this.data.get(1); + int arrowPixelSize = 24; + + return maxProgress != 0 && progress != 0 ? progress * arrowPixelSize / maxProgress : 0; + } + + private void addPlayerInventory(Inventory playerInventory) { + for (int i = 0; i < 3; ++i) { + for (int l = 0; l < 9; ++l) { + this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18)); + } + } + } + + private void addPlayerHotbar(Inventory playerInventory) { + for (int i = 0; i < 9; ++i) { + this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); + } + } +} diff --git a/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceScreen.java b/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceScreen.java new file mode 100644 index 0000000..0857f4d --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/screen/VacuumArcFurnaceScreen.java @@ -0,0 +1,111 @@ +package net.xevianlight.aphelion.screen; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.xevianlight.aphelion.Aphelion; +import net.xevianlight.aphelion.screen.renderer.EnergyDisplayTooltipArea; +import net.xevianlight.aphelion.util.MouseUtil; + +import java.util.Optional; + +public class VacuumArcFurnaceScreen extends AbstractContainerScreen { + + private static final ResourceLocation GUI_TEXTURE = + ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "textures/gui/vacuum_arc_furnace/gui.png"); + private static final ResourceLocation ARROW_TEXTURE = + ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID,"textures/gui/base/arrow_progress.png"); + + private EnergyDisplayTooltipArea energyInfoArea; + + public VacuumArcFurnaceScreen(VacuumArcFurnaceMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + } + + @Override + protected void init() { + super.init(); + + // Gets rid of labels + this.inventoryLabelY = 73; + this.titleLabelY = 5; + this.titleLabelX = 35; + + assignEnergyInfoArea(); + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + RenderSystem.setShaderTexture(0, GUI_TEXTURE); + int x = (width - imageWidth) / 2; + int y = (height - imageHeight) / 2; + + guiGraphics.blit(GUI_TEXTURE, x, y, 0, 0, imageWidth, imageHeight); + + renderProgressArrow(guiGraphics, x, y); + if (menu.blockEntity.getBlockState().getValue(BlockStateProperties.LIT)) { + guiGraphics.blit(GUI_TEXTURE, x + 54, y + 14, 176, 75, 13, 18); + } + + renderEnergyBar(guiGraphics, x , y); + } + + private void renderProgressArrow(GuiGraphics guiGraphics, int x, int y) { + guiGraphics.blit(ARROW_TEXTURE,x + 88, y + 35, 0, 0, menu.getScaledArrowProgress(), 16, 24, 16); + } + + private void renderEnergyBar(GuiGraphics guiGraphics, int x, int y) { + int stored = menu.blockEntity.getTrueEnergyStorage(null).getEnergyStored(); + int max = menu.blockEntity.getTrueEnergyStorage(null).getMaxEnergyStored(); + + int h = (max <= 0) ? 0 : (int) Math.round((stored / (double) max) * 42.0); + h = Math.max(0, Math.min(42, h)); + int w = 14; + + int drawX = x + 9; + int drawY = y + 9 + (42 - h); // move up as it fills + int u = 176; + int v = 15 + (42 - h); // sample lower part of bar texture + + guiGraphics.blit(GUI_TEXTURE, drawX, drawY, u, v, w, h); + } + + private void assignEnergyInfoArea() { + energyInfoArea = new EnergyDisplayTooltipArea(((width - imageWidth) / 2) + 9, + ((height - imageHeight) / 2 ) + 9, menu.blockEntity.getTrueEnergyStorage(null), 14, 42); + } + + private void renderEnergyAreaTooltip(GuiGraphics guiGraphics, int pMouseX, int pMouseY, int x, int y) { + if(isMouseAboveArea(pMouseX, pMouseY, x, y, 9, 9, 14, 42)) { + guiGraphics.renderTooltip(this.font, energyInfoArea.getTooltips(), + Optional.empty(), pMouseX - x, pMouseY - y); + } + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) { + renderBackground(guiGraphics, mouseX, mouseY, delta); + super.render(guiGraphics, mouseX, mouseY, delta); + renderTooltip(guiGraphics, mouseX, mouseY); + } + + @Override + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + super.renderLabels(guiGraphics, mouseX, mouseY); + int x = (width - imageWidth) / 2; + int y = (height - imageHeight) / 2; + + renderEnergyAreaTooltip(guiGraphics, mouseX, mouseY, x, y); + } + + public static boolean isMouseAboveArea(int pMouseX, int pMouseY, int x, int y, int offsetX, int offsetY, int width, int height) { + return MouseUtil.isMouseOver(pMouseX, pMouseY, x + offsetX, y + offsetY, width, height); + } +} diff --git a/src/main/java/net/xevianlight/aphelion/util/AphelionBlockStateProperties.java b/src/main/java/net/xevianlight/aphelion/util/AphelionBlockStateProperties.java new file mode 100644 index 0000000..8c80af5 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/util/AphelionBlockStateProperties.java @@ -0,0 +1,8 @@ +package net.xevianlight.aphelion.util; + +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; + +public class AphelionBlockStateProperties extends BlockStateProperties { + public static final BooleanProperty FORMED = BooleanProperty.create("formed"); +} diff --git a/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java b/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java new file mode 100644 index 0000000..122dc5c --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/util/IMultiblockController.java @@ -0,0 +1,9 @@ +package net.xevianlight.aphelion.util; + +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.Nullable; + +public interface IMultiblockController { + boolean isFormed(); + void setFormed(boolean formed); +} diff --git a/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java b/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java new file mode 100644 index 0000000..98112f6 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/util/IMultiblockPart.java @@ -0,0 +1,10 @@ +package net.xevianlight.aphelion.util; + +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.Nullable; + +public interface IMultiblockPart { + @Nullable BlockPos getControllerPos(); + + void setControllerPos(@Nullable BlockPos pos); +} diff --git a/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java b/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java new file mode 100644 index 0000000..9528fa0 --- /dev/null +++ b/src/main/java/net/xevianlight/aphelion/util/MultiblockHelper.java @@ -0,0 +1,192 @@ +package net.xevianlight.aphelion.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.xevianlight.aphelion.Aphelion; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class MultiblockHelper { + + public record ShapePart (BlockPos offset, Predicate rule) {} + + public static List getPartPositions(BlockState state, BlockPos pos, ShapePart[] shape) { + Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + + List out = new ArrayList<>(shape.length); + + for (ShapePart part : shape) { + BlockPos rotate = rotateY(part.offset, facing); + out.add(pos.offset(rotate)); + } + + return out; + } + + private static BlockPos rotateY(BlockPos offset, Direction facing) { + // Assumes default shape faces north + + return switch (facing) { + case NORTH -> offset; + case EAST -> new BlockPos(-offset.getZ(), offset.getY(), offset.getX()); + case SOUTH -> new BlockPos(-offset.getX(), offset.getY(), -offset.getZ()); + case WEST -> new BlockPos(offset.getZ(), offset.getY(), -offset.getX()); + default -> offset; + }; + } + + private static boolean structureMatches(Level level, BlockState state, BlockPos pos, ShapePart[] shape) { + if (level == null || level.isClientSide) return false; + + Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + + for (ShapePart part : shape) { + BlockPos testPos = pos.offset(rotateY(part.offset(), facing)); + BlockState check = level.getBlockState(testPos); + + if (!part.rule().test(check)) { + return false; + } + } + return true; + } + + public static void tryForm(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + if (level == null || level.isClientSide) return; + + structureMatchesDebug(level, state, pos, shape); + + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof IMultiblockController controller)) return; + + boolean valid = structureMatches(level, state, pos, shape); + + if (valid && !controller.isFormed()) { + controller.setFormed(true); + linkParts(level, state, pos, shape, formedProp); + if (state.hasProperty(AphelionBlockStateProperties.FORMED) && !state.getValue(AphelionBlockStateProperties.FORMED)) { + level.setBlock(pos, state.setValue(AphelionBlockStateProperties.FORMED, true), 3); + } + } else if (!valid && controller.isFormed()) { + unform(level, state, pos, shape, formedProp); + } + } + + public static void unform(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + if (level == null || level.isClientSide) return; + + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof IMultiblockController controller)) return; + + controller.setFormed(false); + if (state.hasProperty(AphelionBlockStateProperties.FORMED) && state.getValue(AphelionBlockStateProperties.FORMED)) { + level.setBlock(pos, state.setValue(AphelionBlockStateProperties.FORMED, false), 3); + } + + unlinkParts(level, state, pos, shape, formedProp); + + } + + public static void unformForRemoval(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + if (level == null || level.isClientSide) return; + + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof IMultiblockController controller)) return; + + controller.setFormed(false); + unlinkParts(level, state, pos, shape, formedProp); + + } + + private static void unlinkParts(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + if (level == null || level.isClientSide) return; + + Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + + for (ShapePart part : shape) { + BlockPos partPos = pos.offset(rotateY(part.offset(), facing)); + BlockState st = level.getBlockState(partPos); + + if (formedProp != null && st.hasProperty(formedProp) && st.getValue(formedProp)) { + level.setBlock(partPos, st.setValue(formedProp, false), 3); + } + + BlockEntity be = level.getBlockEntity(partPos); + if (be instanceof IMultiblockPart mbPart) { + if (pos.equals(mbPart.getControllerPos())) { + mbPart.setControllerPos(null); + be.setChanged(); + } + } + } + + } + + private static void linkParts(Level level, BlockState state, BlockPos pos, ShapePart[] shape, @Nullable BooleanProperty formedProp) { + if (level == null || level.isClientSide) return; + + Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + + for (ShapePart part : shape) { + BlockPos partPos = pos.offset(rotateY(part.offset(), facing)); + BlockState st = level.getBlockState(partPos); + + Aphelion.LOGGER.debug("[Multiblock] partPos={} block={} hasFormed={} formedVal={}", + partPos, + st.getBlock(), + (formedProp != null && st.hasProperty(formedProp)), + (formedProp != null && st.hasProperty(formedProp)) ? st.getValue(formedProp) : "n/a" + ); + + if (formedProp != null && st.hasProperty(formedProp) && !st.getValue(formedProp)) { + level.setBlock(partPos, st.setValue(formedProp, true), 3); + st = level.getBlockState(partPos); + } + + BlockEntity be = level.getBlockEntity(partPos); + if (be instanceof IMultiblockPart mbPart) { + if (!pos.equals(mbPart.getControllerPos())) { + mbPart.setControllerPos(pos); + be.setChanged(); + } + } + } + } + + public static boolean structureMatchesDebug( + Level level, + BlockState controllerState, + BlockPos controllerPos, + ShapePart[] shape + ) { + Direction facing = controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING); + + for (ShapePart part : shape) { + BlockPos rotated = rotateY(part.offset(), facing); + BlockPos worldPos = controllerPos.offset(rotated); + + BlockState found = level.getBlockState(worldPos); + + if (!part.rule().test(found)) { + Aphelion.LOGGER.debug("[Multiblock] FAIL at offset=" + part.offset() + + " facing=" + facing + + " rotated=" + rotated + + " worldPos=" + worldPos + + " found=" + found.getBlock()); + return false; + } + } + + Aphelion.LOGGER.debug("[Multiblock] OK facing={} controllerPos={}", controllerState.getValue(BlockStateProperties.HORIZONTAL_FACING), controllerPos); + return true; + } + +} diff --git a/src/main/resources/assets/aphelion/blockstates/electric_arc_furnace.json b/src/main/resources/assets/aphelion/blockstates/electric_arc_furnace.json deleted file mode 100644 index cae5000..0000000 --- a/src/main/resources/assets/aphelion/blockstates/electric_arc_furnace.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "variants": { - "facing=east,formed=false,lit=false": { - "model": "aphelion:block/electric_arc_furnace", - "y": 90 - }, - "facing=east,formed=false,lit=true": { - "model": "aphelion:block/electric_arc_furnace", - "y": 90 - }, - "facing=east,formed=true,lit=false": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 90 - }, - "facing=east,formed=true,lit=true": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 90 - }, - "facing=north,formed=false,lit=false": { - "model": "aphelion:block/electric_arc_furnace" - }, - "facing=north,formed=false,lit=true": { - "model": "aphelion:block/electric_arc_furnace" - }, - "facing=north,formed=true,lit=false": { - "model": "aphelion:block/electric_arc_furnace_formed" - }, - "facing=north,formed=true,lit=true": { - "model": "aphelion:block/electric_arc_furnace_formed" - }, - "facing=south,formed=false,lit=false": { - "model": "aphelion:block/electric_arc_furnace", - "y": 180 - }, - "facing=south,formed=false,lit=true": { - "model": "aphelion:block/electric_arc_furnace", - "y": 180 - }, - "facing=south,formed=true,lit=false": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 180 - }, - "facing=south,formed=true,lit=true": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 180 - }, - "facing=west,formed=false,lit=false": { - "model": "aphelion:block/electric_arc_furnace", - "y": 270 - }, - "facing=west,formed=false,lit=true": { - "model": "aphelion:block/electric_arc_furnace", - "y": 270 - }, - "facing=west,formed=true,lit=false": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 270 - }, - "facing=west,formed=true,lit=true": { - "model": "aphelion:block/electric_arc_furnace_formed", - "y": 270 - } - } -} \ No newline at end of file diff --git a/src/main/resources/assets/aphelion/blockstates/vacuum_arc_furnace_controller.json b/src/main/resources/assets/aphelion/blockstates/vacuum_arc_furnace_controller.json new file mode 100644 index 0000000..bd3d0c1 --- /dev/null +++ b/src/main/resources/assets/aphelion/blockstates/vacuum_arc_furnace_controller.json @@ -0,0 +1,64 @@ +{ + "variants": { + "facing=east,formed=false,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 90 + }, + "facing=east,formed=false,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 90 + }, + "facing=east,formed=true,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 90 + }, + "facing=east,formed=true,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 90 + }, + "facing=north,formed=false,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller" + }, + "facing=north,formed=false,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller" + }, + "facing=north,formed=true,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed" + }, + "facing=north,formed=true,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed" + }, + "facing=south,formed=false,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 180 + }, + "facing=south,formed=false,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 180 + }, + "facing=south,formed=true,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 180 + }, + "facing=south,formed=true,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 180 + }, + "facing=west,formed=false,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 270 + }, + "facing=west,formed=false,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller", + "y": 270 + }, + "facing=west,formed=true,lit=false": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 270 + }, + "facing=west,formed=true,lit=true": { + "model": "aphelion:block/vacuum_arc_furnace_controller_formed", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/aphelion/models/block/electric_arc_furnace.json b/src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller.json similarity index 70% rename from src/main/resources/assets/aphelion/models/block/electric_arc_furnace.json rename to src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller.json index 07f6a0a..07e0c9c 100644 --- a/src/main/resources/assets/aphelion/models/block/electric_arc_furnace.json +++ b/src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller.json @@ -1,7 +1,7 @@ { "parent": "minecraft:block/orientable", "textures": { - "front": "aphelion:block/electric_arc_furnace_front", + "front": "aphelion:block/vacuum_arc_furnace_controller_front", "side": "minecraft:block/blast_furnace_side", "top": "minecraft:block/blast_furnace_top" } diff --git a/src/main/resources/assets/aphelion/models/block/electric_arc_furnace_formed.json b/src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller_formed.json similarity index 68% rename from src/main/resources/assets/aphelion/models/block/electric_arc_furnace_formed.json rename to src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller_formed.json index 9b59082..ff51c38 100644 --- a/src/main/resources/assets/aphelion/models/block/electric_arc_furnace_formed.json +++ b/src/main/resources/assets/aphelion/models/block/vacuum_arc_furnace_controller_formed.json @@ -1,7 +1,7 @@ { "parent": "minecraft:block/orientable", "textures": { - "front": "aphelion:block/electric_arc_furnace_front_formed", + "front": "aphelion:block/vacuum_arc_furnace_controller_front_formed", "side": "minecraft:block/blast_furnace_side", "top": "minecraft:block/blast_furnace_top" } diff --git a/src/main/resources/assets/aphelion/textures/block/vacuum_arc_furnace_controller_front.png b/src/main/resources/assets/aphelion/textures/block/vacuum_arc_furnace_controller_front.png new file mode 100644 index 0000000..58a8d4b Binary files /dev/null and b/src/main/resources/assets/aphelion/textures/block/vacuum_arc_furnace_controller_front.png differ diff --git a/src/main/resources/assets/aphelion/textures/block/electric_arc_furnace_front_formed.png b/src/main/resources/assets/aphelion/textures/block/vacuum_arc_furnace_controller_front_formed.png similarity index 100% rename from src/main/resources/assets/aphelion/textures/block/electric_arc_furnace_front_formed.png rename to src/main/resources/assets/aphelion/textures/block/vacuum_arc_furnace_controller_front_formed.png diff --git a/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/gui.png b/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/gui.png new file mode 100644 index 0000000..7a85d16 Binary files /dev/null and b/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/gui.png differ diff --git a/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/jei.png b/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/jei.png new file mode 100644 index 0000000..4b4c1ed Binary files /dev/null and b/src/main/resources/assets/aphelion/textures/gui/vacuum_arc_furnace/jei.png differ