Basic development

This commit is contained in:
XevianLight
2026-01-16 17:51:41 -07:00
parent b0131011e5
commit 48e5499ad3
119 changed files with 3613 additions and 185 deletions

View File

@@ -0,0 +1,25 @@
package net.xevianlight.aphelion.block.custom;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType;
public class BlockSteel extends Block {
public BlockSteel(Properties properties) {
super(properties);
}
public static Properties getProperties() {
return Properties
.of()
.sound(SoundType.NETHERITE_BLOCK)
.destroyTime(2f)
.explosionResistance(10f)
.requiresCorrectToolForDrops();
}
public static Item.Properties getItemProperties() {
return new Item.Properties();
}
}

View File

@@ -0,0 +1,93 @@
package net.xevianlight.aphelion.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.portal.DimensionTransition;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.xevianlight.aphelion.block.entity.custom.DimensionChangerBlockEntity;
import org.jetbrains.annotations.Nullable;
public class DimensionChangerBlock extends BaseEntityBlock {
public DimensionChangerBlock(Properties properties) {
super(properties);
}
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {
return null;
}
public static Properties getProperties() {
return Properties
.of()
.sound(SoundType.ANVIL)
.destroyTime(2f)
.explosionResistance(10f)
.requiresCorrectToolForDrops();
}
public static Item.Properties getItemProperties() {
return new Item.Properties().stacksTo(3);
}
@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 DimensionChangerBlockEntity dimensionChangerBlockEntity) {
ServerLevel sourceDim = serverPlayer.serverLevel();
ServerLevel targetDim = serverPlayer.server.getLevel(Level.NETHER);
serverPlayer.changeDimension(new DimensionTransition(
targetDim,
serverPlayer.position().multiply(
sourceDim.dimensionType().coordinateScale() / targetDim.dimensionType().coordinateScale(),
1,
sourceDim.dimensionType().coordinateScale() / targetDim.dimensionType().coordinateScale()),
Vec3.ZERO, serverPlayer.getYRot(),
serverPlayer.getXRot(),
DimensionTransition.PLAY_PORTAL_SOUND
));
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
@Override
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
super.tick(state, level, pos, random);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new DimensionChangerBlockEntity(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 (state.getBlock() != newState.getBlock()) {
BlockEntity blockEntity= level.getBlockEntity(pos);
if (blockEntity instanceof DimensionChangerBlockEntity dimensionChangerBlockEntity) {
dimensionChangerBlockEntity.drops();
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
}

View File

@@ -0,0 +1,170 @@
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.ElectricArcFurnaceEntity;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ElectricArcFurnace extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
public ElectricArcFurnace(Properties properties) {
super(properties);
}
public static final MapCodec<ElectricArcFurnace> CODEC = simpleCodec(ElectricArcFurnace::new);
@Override
protected MapCodec<? extends BaseEntityBlock> 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 ElectricArcFurnaceEntity electricArcFurnaceEntity) {
serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new ElectricArcFurnaceEntity(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 (state.getBlock() != newState.getBlock()) {
BlockEntity blockEntity= level.getBlockEntity(pos);
if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
electricArcFurnaceEntity.drops();
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
public static int getRedstoneSignalFromItemHandler(@javax.annotation.Nullable ItemStackHandler itemStackHandler, List<Integer> 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<Integer> slots = new ArrayList<>();
slots.add(0);
slots.add(1);
slots.add(2);
slots.add(3);
ElectricArcFurnaceEntity electricArcFurnaceEntity = ((ElectricArcFurnaceEntity) level.getBlockEntity(pos));
return getRedstoneSignalFromItemHandler(electricArcFurnaceEntity.inventory, slots);
}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (level.isClientSide) {
return null;
}
return createTickerHelper(blockEntityType, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), (level1, blockPos, blockState, electricArcFurnaceEntity) -> electricArcFurnaceEntity.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);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
}

View File

@@ -0,0 +1,138 @@
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.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.SoundType;
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.phys.BlockHitResult;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class TestBlock extends BaseEntityBlock {
public TestBlock(Properties properties) {
super(properties);
}
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {
return null;
}
public static Properties getProperties() {
return Properties
.of()
.sound(SoundType.ANVIL)
.destroyTime(2f)
.explosionResistance(10f)
.requiresCorrectToolForDrops();
}
public static Item.Properties getItemProperties() {
return new Item.Properties().stacksTo(3);
}
@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 TestBlockEntity testBlockEntity) {
serverPlayer.openMenu(new SimpleMenuProvider(testBlockEntity, Component.literal("Test Block")), pos);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new TestBlockEntity(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 (state.getBlock() != newState.getBlock()) {
BlockEntity blockEntity= level.getBlockEntity(pos);
if (blockEntity instanceof TestBlockEntity testBlockEntity) {
testBlockEntity.drops();
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
public static int getRedstoneSignalFromItemHandler(@javax.annotation.Nullable ItemStackHandler itemStackHandler, List<Integer> 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<Integer> slots = new ArrayList<>();
slots.add(0);
TestBlockEntity testBlockEntity = ((TestBlockEntity) level.getBlockEntity(pos));
return getRedstoneSignalFromItemHandler(testBlockEntity.inventory, slots);
}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (level.isClientSide) {
return null;
}
return createTickerHelper(blockEntityType, ModBlockEntities.TEST_BLOCK_ENTITY.get(), (level1, blockPos, blockState, testBlockEntity) -> testBlockEntity.tick(level1, blockPos, blockState));
}
}

View File

@@ -0,0 +1,95 @@
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.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
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.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import net.xevianlight.aphelion.screen.TestBlockMenu;
import net.xevianlight.aphelion.util.SidedSlotHandler;
import org.jetbrains.annotations.Nullable;
public class DimensionChangerBlockEntity extends BlockEntity implements MenuProvider {
private final int SIZE = 1;
private NonNullList<ItemStack> items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
public DimensionChangerBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.DIMENSION_CHANGER_BLOCK_ENTITY.get(), pos, blockState);
}
public final ItemStackHandler inventory = new ItemStackHandler(SIZE) {
@Override
protected void onContentsChanged(int slot) {
setChanged();
if(!level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
public void sendUpdate() {
setChanged();
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
private final IItemHandler topHandler = new SidedSlotHandler(inventory, new int[]{0}, true, false);
private final IItemHandler bottomHandler = new SidedSlotHandler(inventory, new int[]{0}, false, true);
private final IItemHandler jadeHandler = new SidedSlotHandler(inventory, new int[]{0}, false, false);
public IItemHandler getItemHandler(Direction direction) {
if (direction == Direction.UP) return topHandler;
if (direction == Direction.DOWN) return bottomHandler;
if (direction == null) return jadeHandler;
return null;
}
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 pTag, HolderLookup.Provider pRegistries) {
pTag.put("inventory", inventory.serializeNBT(pRegistries));
super.saveAdditional(pTag, pRegistries);
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.loadAdditional(pTag, pRegistries);
inventory.deserializeNBT(pRegistries, pTag.getCompound("inventory"));
}
@Override
public Component getDisplayName() {
return null;
}
@Override
public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new TestBlockMenu(i, inventory, this);
}
}

View File

@@ -0,0 +1,311 @@
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.core.component.DataComponents;
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.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
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.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.EnergyStorage;
import net.neoforged.neoforge.energy.IEnergyStorage;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
import net.xevianlight.aphelion.block.entity.energy.ModEnergyUtil;
import net.xevianlight.aphelion.client.dimension.DimensionRendererCache;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu;
import net.xevianlight.aphelion.util.SidedSlotHandler;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider {
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 Blasting_ENERGY_COST = 20;
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;
public ElectricArcFurnaceEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int index) {
return switch (index) {
case 0 -> ElectricArcFurnaceEntity.this.progress;
case 1 -> ElectricArcFurnaceEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int index, int pValue) {
switch (index) {
case 0: ElectricArcFurnaceEntity.this.progress = pValue;
case 1: ElectricArcFurnaceEntity.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 (inventory.getStackInSlot(SECONDARY_INPUT_SLOT).isEmpty()) {
if (hasFurnaceRecipe(INPUT_SLOT) && hasEnoughEnergyToCraft(Blasting_ENERGY_COST)) {
progress++;
useEnergyForBlasting();
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(Blasting_ENERGY_COST)) {
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
progress = progress > 0 ? progress - 1 : 0;
} else {
resetProgress();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
}
chargeFromItem();
}
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<RecipeHolder<BlastingRecipe>> 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 resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void useEnergyForBlasting() {
this.ENERGY_STORAGE.extractEnergy(Blasting_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() {
return this.progress >= this.maxProgress;
}
private boolean hasFurnaceRecipe(int slot) {
Optional<RecipeHolder<BlastingRecipe>> 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<RecipeHolder<BlastingRecipe>> 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[]{0,1}, true, true);
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) {
return fullHandler;
}
public IEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.ENERGY_STORAGE;
}
private final ModEnergyStorage ENERGY_STORAGE = createEnergyStorage();
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(ENERGY_CAPACITY, MAX_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) {
return new ElectricArcFurnaceMenu(i, inventory, this, this.data);
}
@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider pRegistries) {
return saveWithoutMetadata(pRegistries);
}
}

View File

@@ -0,0 +1,100 @@
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.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.*;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import net.xevianlight.aphelion.screen.TestBlockMenu;
import net.xevianlight.aphelion.util.SidedSlotHandler;
import org.jetbrains.annotations.Nullable;
public class TestBlockEntity extends BlockEntity implements MenuProvider {
private final int SIZE = 1;
private NonNullList<ItemStack> items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
public TestBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.TEST_BLOCK_ENTITY.get(), pos, blockState);
}
public final ItemStackHandler inventory = new ItemStackHandler(SIZE) {
@Override
protected void onContentsChanged(int slot) {
setChanged();
if(!level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
public void tick(Level level, BlockPos pos, BlockState blockState) {
inventory.insertItem(0, new ItemStack(Items.DIAMOND), false);
}
public void sendUpdate() {
setChanged();
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
private final IItemHandler topHandler = new SidedSlotHandler(inventory, new int[]{0}, true, false);
private final IItemHandler bottomHandler = new SidedSlotHandler(inventory, new int[]{0}, false, true);
private final IItemHandler jadeHandler = new SidedSlotHandler(inventory, new int[]{0}, false, false);
public IItemHandler getItemHandler(Direction direction) {
if (direction == Direction.UP) return topHandler;
if (direction == Direction.DOWN) return bottomHandler;
if (direction == null) return jadeHandler;
return null;
}
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 pTag, HolderLookup.Provider pRegistries) {
pTag.put("inventory", inventory.serializeNBT(pRegistries));
super.saveAdditional(pTag, pRegistries);
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.loadAdditional(pTag, pRegistries);
inventory.deserializeNBT(pRegistries, pTag.getCompound("inventory"));
}
@Override
public Component getDisplayName() {
return null;
}
@Override
public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new TestBlockMenu(i, inventory, this);
}
}

View File

@@ -0,0 +1,93 @@
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.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.*;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import org.jetbrains.annotations.Nullable;
public class TestBlockEntity2 extends BaseContainerBlockEntity implements WorldlyContainer {
// The constructor, like before.
public TestBlockEntity2 (BlockPos pos, BlockState blockState) {
super(ModBlockEntities.TEST_BLOCK_ENTITY.get(), pos, blockState);
}
// The container size. This can of course be any value you want.
public static final int SIZE = 9;
// Our item stack list. This is not final due to #setItems existing.
private NonNullList<ItemStack> items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
// The container size, like before.
@Override
public int getContainerSize() {
return SIZE;
}
// The getter for our item stack list.
@Override
protected NonNullList<ItemStack> getItems() {
return items;
}
// The setter for our item stack list.
@Override
protected void setItems(NonNullList<ItemStack> items) {
this.items = items;
}
// The display name of the menu. Don't forget to add a translation!
@Override
protected Component getDefaultName() {
return Component.translatable("container.examplemod.myblockentity");
}
// The menu to create from this container. See below for what to return here.
@Override
protected AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return null;
}
@Override
public int[] getSlotsForFace(Direction direction) {
return new int[] {0,1,2,3,4,5,6,7,8};
}
@Override
public boolean canPlaceItemThroughFace(int i, ItemStack itemStack, @Nullable Direction direction) {
return true;
}
@Override
public boolean canTakeItemThroughFace(int i, ItemStack itemStack, Direction direction) {
return true;
}
@Override
public void loadAdditional(CompoundTag nbt, HolderLookup.Provider provider) {
super.loadAdditional(nbt, provider);
this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
ContainerHelper.loadAllItems(nbt, this.items, provider);
}
@Override
protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider provider) {
super.saveAdditional(nbt, provider);
ContainerHelper.saveAllItems(nbt, this.items, provider);
}
public void dropContents() {
for (int i = 0; i < items.size(); i++) {
ItemStack stack = getItem(i);
Containers.dropItemStack(level, (double) worldPosition.getX(), (double) worldPosition.getY(), (double) worldPosition.getZ(), stack);
}
}
}

View File

@@ -0,0 +1,37 @@
package net.xevianlight.aphelion.block.entity.energy;
import net.neoforged.neoforge.energy.EnergyStorage;
public abstract class ModEnergyStorage extends EnergyStorage {
public ModEnergyStorage(int capacity, int maxTransfer) {
super(capacity, maxTransfer);
}
@Override
public int extractEnergy(int maxExtract, boolean simulate) {
int extractedEnergy = super.extractEnergy(maxExtract, simulate);
if(extractedEnergy != 0) {
onEnergyChanged();
}
return extractedEnergy;
}
@Override
public int receiveEnergy(int maxReceive, boolean simulate) {
int receiveEnergy = super.receiveEnergy(maxReceive, simulate);
if(receiveEnergy != 0) {
onEnergyChanged();
}
return receiveEnergy;
}
public int setEnergy(int energy) {
this.energy = energy;
return energy;
}
public abstract void onEnergyChanged();
}

View File

@@ -0,0 +1,43 @@
package net.xevianlight.aphelion.block.entity.energy;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.IEnergyStorage;
public class ModEnergyUtil {
public static boolean move(BlockPos from, BlockPos to, int amount, Level level) {
IEnergyStorage fromStorage = level.getCapability(Capabilities.EnergyStorage.BLOCK, from, null);
IEnergyStorage toStorage = level.getCapability(Capabilities.EnergyStorage.BLOCK, to, null);
if(canEnergyStorageExtractThisAmount(fromStorage, amount)) {
return false;
}
if(canEnergyStorageStillReceiveEnergy(toStorage)) {
return false;
}
int maxAmountToReceive = toStorage.receiveEnergy(amount, true);
int extractedEnergy = fromStorage.extractEnergy(maxAmountToReceive, false);
toStorage.receiveEnergy(extractedEnergy, false);
return true;
}
private static boolean canEnergyStorageStillReceiveEnergy(IEnergyStorage toStorage) {
// No more Energy to draw or cannot extract
return toStorage.getEnergyStored() >= toStorage.getMaxEnergyStored() || !toStorage.canReceive();
}
private static boolean canEnergyStorageExtractThisAmount(IEnergyStorage fromStorage, int amount) {
// No more Space to receive or cannot receive
return fromStorage.getEnergyStored() <= 0 || fromStorage.getEnergyStored() < amount || !fromStorage.canExtract();
}
public static boolean doesBlockHaveEnergyStorage(BlockPos positionToCheck, Level level) {
return level.getBlockEntity(positionToCheck) != null
&& level.getCapability(Capabilities.EnergyStorage.BLOCK, positionToCheck, null) != null;
}
}