435 lines
16 KiB
Java
435 lines
16 KiB
Java
package com.atsuishio.superbwarfare.block.entity;
|
|
|
|
import com.atsuishio.superbwarfare.block.ChargingStationBlock;
|
|
import com.atsuishio.superbwarfare.init.ModBlockEntities;
|
|
import com.atsuishio.superbwarfare.menu.ChargingStationMenu;
|
|
import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.NonNullList;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.IntTag;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.world.Container;
|
|
import net.minecraft.world.ContainerHelper;
|
|
import net.minecraft.world.MenuProvider;
|
|
import net.minecraft.world.WorldlyContainer;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
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.crafting.RecipeType;
|
|
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.phys.AABB;
|
|
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
|
|
import net.neoforged.neoforge.capabilities.Capabilities;
|
|
import net.neoforged.neoforge.capabilities.ICapabilityProvider;
|
|
import net.neoforged.neoforge.energy.EnergyStorage;
|
|
import net.neoforged.neoforge.energy.IEnergyStorage;
|
|
import net.neoforged.neoforge.items.IItemHandler;
|
|
import net.neoforged.neoforge.items.wrapper.SidedInvWrapper;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Energy Data Slot Code based on @GoryMoon's Chargers
|
|
*/
|
|
public class ChargingStationBlockEntity extends BlockEntity implements WorldlyContainer, MenuProvider {
|
|
|
|
protected static final int SLOT_FUEL = 0;
|
|
protected static final int SLOT_CHARGE = 1;
|
|
|
|
public static final int MAX_ENERGY = 4000000;
|
|
public static final int MAX_DATA_COUNT = 4;
|
|
public static final int DEFAULT_FUEL_TIME = 1600;
|
|
public static final int CHARGE_SPEED = 128;
|
|
public static final int CHARGE_OTHER_SPEED = 100000;
|
|
public static final int CHARGE_RADIUS = 8;
|
|
|
|
protected NonNullList<ItemStack> items = NonNullList.withSize(2, ItemStack.EMPTY);
|
|
|
|
|
|
private BlockCapabilityCache<IEnergyStorage, @Nullable Direction> capCache;
|
|
|
|
@Override
|
|
public void onLoad() {
|
|
super.onLoad();
|
|
|
|
if (level != null && !level.isClientSide) {
|
|
this.capCache = BlockCapabilityCache.create(
|
|
Capabilities.EnergyStorage.BLOCK,
|
|
(ServerLevel) level,
|
|
this.getBlockPos(),
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
public int fuelTick = 0;
|
|
public int maxFuelTick = DEFAULT_FUEL_TIME;
|
|
public boolean showRange = false;
|
|
|
|
protected final ContainerEnergyData dataAccess = new ContainerEnergyData() {
|
|
public int get(int pIndex) {
|
|
return switch (pIndex) {
|
|
case 0 -> ChargingStationBlockEntity.this.fuelTick;
|
|
case 1 -> ChargingStationBlockEntity.this.maxFuelTick;
|
|
case 2 -> {
|
|
var level = ChargingStationBlockEntity.this.level;
|
|
if (level == null) yield 0;
|
|
|
|
var cap = level.getCapability(Capabilities.EnergyStorage.BLOCK, ChargingStationBlockEntity.this.getBlockPos(), null);
|
|
if (cap == null) yield 0;
|
|
|
|
yield cap.getEnergyStored();
|
|
}
|
|
case 3 -> ChargingStationBlockEntity.this.showRange ? 1 : 0;
|
|
default -> 0;
|
|
};
|
|
}
|
|
|
|
public void set(int pIndex, int pValue) {
|
|
switch (pIndex) {
|
|
case 0:
|
|
ChargingStationBlockEntity.this.fuelTick = pValue;
|
|
break;
|
|
case 1:
|
|
ChargingStationBlockEntity.this.maxFuelTick = pValue;
|
|
break;
|
|
case 2:
|
|
var level = ChargingStationBlockEntity.this.level;
|
|
if (level == null) return;
|
|
|
|
var cap = level.getCapability(Capabilities.EnergyStorage.BLOCK, ChargingStationBlockEntity.this.getBlockPos(), null);
|
|
if (cap == null) return;
|
|
|
|
cap.receiveEnergy(pValue, false);
|
|
break;
|
|
case 3:
|
|
ChargingStationBlockEntity.this.showRange = pValue == 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public int getCount() {
|
|
return MAX_DATA_COUNT;
|
|
}
|
|
};
|
|
|
|
public ChargingStationBlockEntity(BlockPos pos, BlockState state) {
|
|
super(ModBlockEntities.CHARGING_STATION.get(), pos, state);
|
|
}
|
|
|
|
public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, ChargingStationBlockEntity blockEntity) {
|
|
if (blockEntity.showRange != pState.getValue(ChargingStationBlock.SHOW_RANGE)) {
|
|
pLevel.setBlockAndUpdate(pPos, pState.setValue(ChargingStationBlock.SHOW_RANGE, blockEntity.showRange));
|
|
setChanged(pLevel, pPos, pState);
|
|
}
|
|
|
|
var handler = blockEntity.capCache.getCapability();
|
|
if (handler == null) return;
|
|
|
|
int energy = handler.getEnergyStored();
|
|
if (energy > 0) {
|
|
blockEntity.chargeEntity(handler);
|
|
}
|
|
if (handler.getEnergyStored() > 0) {
|
|
blockEntity.chargeItemStack(handler);
|
|
}
|
|
if (handler.getEnergyStored() > 0) {
|
|
blockEntity.chargeBlock(handler);
|
|
}
|
|
|
|
if (blockEntity.fuelTick > 0) {
|
|
blockEntity.fuelTick--;
|
|
if (energy < handler.getMaxEnergyStored()) {
|
|
handler.receiveEnergy(CHARGE_SPEED, false);
|
|
}
|
|
} else if (!blockEntity.getItem(SLOT_FUEL).isEmpty()) {
|
|
if (handler.getEnergyStored() >= handler.getMaxEnergyStored()) return;
|
|
|
|
ItemStack fuel = blockEntity.getItem(SLOT_FUEL);
|
|
int burnTime = fuel.getBurnTime(RecipeType.SMELTING);
|
|
|
|
var fuelEnergy = fuel.getCapability(Capabilities.EnergyStorage.ITEM);
|
|
|
|
if (fuelEnergy != null) {
|
|
// 优先当作电池处理
|
|
var energyToExtract = Math.min(CHARGE_OTHER_SPEED, handler.getMaxEnergyStored() - handler.getEnergyStored());
|
|
if (fuelEnergy.canExtract() && handler.canReceive()) {
|
|
handler.receiveEnergy(fuelEnergy.extractEnergy(energyToExtract, false), false);
|
|
}
|
|
|
|
blockEntity.setChanged();
|
|
} else if (burnTime > 0) {
|
|
// 其次尝试作为燃料处理
|
|
blockEntity.fuelTick = burnTime;
|
|
blockEntity.maxFuelTick = burnTime;
|
|
|
|
if (fuel.hasCraftingRemainingItem()) {
|
|
if (fuel.getCount() <= 1) {
|
|
blockEntity.setItem(SLOT_FUEL, fuel.getCraftingRemainingItem());
|
|
} else {
|
|
ItemStack copy = fuel.getCraftingRemainingItem().copy();
|
|
copy.setCount(1);
|
|
|
|
ItemEntity itemEntity = new ItemEntity(pLevel,
|
|
pPos.getX() + 0.5,
|
|
pPos.getY() + 0.2,
|
|
pPos.getZ() + 0.5,
|
|
copy);
|
|
pLevel.addFreshEntity(itemEntity);
|
|
|
|
fuel.shrink(1);
|
|
}
|
|
} else {
|
|
fuel.shrink(1);
|
|
}
|
|
|
|
blockEntity.setChanged();
|
|
} else if (fuel.get(DataComponents.FOOD) != null) {
|
|
// 最后作为食物处理
|
|
var foodComponent = fuel.get(DataComponents.FOOD);
|
|
if (foodComponent == null) return;
|
|
|
|
int nutrition = foodComponent.nutrition();
|
|
float saturation = foodComponent.saturation() * 2.0f * nutrition;
|
|
int tick = nutrition * 80 + (int) (saturation * 200);
|
|
|
|
if (fuel.hasCraftingRemainingItem()) {
|
|
tick += 400;
|
|
}
|
|
|
|
fuel.shrink(1);
|
|
|
|
blockEntity.fuelTick = tick;
|
|
blockEntity.maxFuelTick = tick;
|
|
blockEntity.setChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void chargeEntity(IEnergyStorage handler) {
|
|
if (this.level == null) return;
|
|
if (this.level.getGameTime() % 20 != 0) return;
|
|
|
|
List<Entity> entities = this.level.getEntitiesOfClass(Entity.class, new AABB(this.getBlockPos()).inflate(CHARGE_RADIUS));
|
|
entities.forEach(entity -> {
|
|
var cap = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
|
|
if (cap == null || !cap.canReceive()) return;
|
|
|
|
int charged = cap.receiveEnergy(Math.min(handler.getEnergyStored(), CHARGE_OTHER_SPEED * 20), false);
|
|
handler.extractEnergy(charged, false);
|
|
});
|
|
this.setChanged();
|
|
}
|
|
|
|
private void chargeItemStack(IEnergyStorage handler) {
|
|
ItemStack stack = this.getItem(SLOT_CHARGE);
|
|
if (stack.isEmpty()) return;
|
|
|
|
var consumer = stack.getCapability(Capabilities.EnergyStorage.ITEM);
|
|
if (consumer != null) {
|
|
if (consumer.getEnergyStored() < consumer.getMaxEnergyStored()) {
|
|
int charged = consumer.receiveEnergy(Math.min(CHARGE_OTHER_SPEED, handler.getEnergyStored()), false);
|
|
handler.extractEnergy(Math.min(charged, handler.getEnergyStored()), false);
|
|
}
|
|
}
|
|
this.setChanged();
|
|
}
|
|
|
|
private void chargeBlock(IEnergyStorage handler) {
|
|
if (this.level == null) return;
|
|
|
|
for (Direction direction : Direction.values()) {
|
|
var blockEntity = this.level.getBlockEntity(this.getBlockPos().relative(direction));
|
|
if (blockEntity == null) return;
|
|
|
|
var energy = level.getCapability(Capabilities.EnergyStorage.BLOCK, blockEntity.getBlockPos(), direction);
|
|
if (energy == null || !(blockEntity instanceof ChargingStationBlockEntity)) return;
|
|
|
|
if (energy.canReceive() && energy.getEnergyStored() < energy.getMaxEnergyStored()) {
|
|
int receiveEnergy = energy.receiveEnergy(Math.min(handler.getEnergyStored(), CHARGE_OTHER_SPEED), false);
|
|
handler.extractEnergy(receiveEnergy, false);
|
|
|
|
blockEntity.setChanged();
|
|
this.setChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public NonNullList<ItemStack> getItems() {
|
|
return this.items;
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
|
super.loadAdditional(tag, registries);
|
|
|
|
if (tag.contains("Energy") && this.capCache.getCapability() != null) {
|
|
var energy = tag.get("Energy");
|
|
if (energy instanceof IntTag) {
|
|
((EnergyStorage) this.capCache.getCapability()).deserializeNBT(registries, energy);
|
|
}
|
|
}
|
|
this.fuelTick = tag.getInt("FuelTick");
|
|
this.maxFuelTick = tag.getInt("MaxFuelTick");
|
|
this.showRange = tag.getBoolean("ShowRange");
|
|
this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
|
ContainerHelper.loadAllItems(tag, this.items, registries);
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
|
super.saveAdditional(tag, registries);
|
|
|
|
if (this.capCache != null && this.capCache.getCapability() != null) {
|
|
tag.put("Energy", IntTag.valueOf(this.capCache.getCapability().getEnergyStored()));
|
|
}
|
|
tag.putInt("FuelTick", this.fuelTick);
|
|
tag.putInt("MaxFuelTick", this.maxFuelTick);
|
|
tag.putBoolean("ShowRange", this.showRange);
|
|
ContainerHelper.saveAllItems(tag, this.items, registries);
|
|
}
|
|
|
|
@Override
|
|
public int @NotNull [] getSlotsForFace(@NotNull Direction pSide) {
|
|
return new int[]{SLOT_FUEL};
|
|
}
|
|
|
|
@Override
|
|
public boolean canPlaceItemThroughFace(int pIndex, @NotNull ItemStack pItemStack, @Nullable Direction pDirection) {
|
|
return pIndex == SLOT_FUEL;
|
|
}
|
|
|
|
@Override
|
|
public boolean canTakeItemThroughFace(int pIndex, @NotNull ItemStack pStack, @NotNull Direction pDirection) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int getContainerSize() {
|
|
return this.items.size();
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty() {
|
|
for (ItemStack itemstack : this.items) {
|
|
if (!itemstack.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public @NotNull ItemStack getItem(int pSlot) {
|
|
return this.items.get(pSlot);
|
|
}
|
|
|
|
@Override
|
|
public @NotNull ItemStack removeItem(int pSlot, int pAmount) {
|
|
return ContainerHelper.removeItem(this.items, pSlot, pAmount);
|
|
}
|
|
|
|
@Override
|
|
public @NotNull ItemStack removeItemNoUpdate(int pSlot) {
|
|
return ContainerHelper.takeItem(this.items, pSlot);
|
|
}
|
|
|
|
@Override
|
|
public void setItem(int pSlot, ItemStack pStack) {
|
|
ItemStack itemstack = this.items.get(pSlot);
|
|
boolean flag = !pStack.isEmpty() && ItemStack.isSameItem(itemstack, pStack);
|
|
this.items.set(pSlot, pStack);
|
|
if (pStack.getCount() > this.getMaxStackSize()) {
|
|
pStack.setCount(this.getMaxStackSize());
|
|
}
|
|
|
|
if (pSlot == 0 && !flag) {
|
|
this.setChanged();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean stillValid(@NotNull Player pPlayer) {
|
|
return Container.stillValidBlockEntity(this, pPlayer);
|
|
}
|
|
|
|
@Override
|
|
public void clearContent() {
|
|
this.items.clear();
|
|
}
|
|
|
|
@Override
|
|
public @NotNull Component getDisplayName() {
|
|
return Component.translatable("container.superbwarfare.charging_station");
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public AbstractContainerMenu createMenu(int pContainerId, @NotNull Inventory pPlayerInventory, @NotNull Player pPlayer) {
|
|
return new ChargingStationMenu(pContainerId, pPlayerInventory, this, this.dataAccess);
|
|
}
|
|
|
|
@Override
|
|
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
}
|
|
|
|
@Override
|
|
public @NotNull CompoundTag getUpdateTag(HolderLookup.@NotNull Provider registries) {
|
|
CompoundTag compoundtag = new CompoundTag();
|
|
ContainerHelper.saveAllItems(compoundtag, this.items, registries);
|
|
compoundtag.putBoolean("ShowRange", this.showRange);
|
|
return compoundtag;
|
|
}
|
|
|
|
public static class EnergyStorageProvider implements ICapabilityProvider<ChargingStationBlockEntity, Direction, IEnergyStorage> {
|
|
|
|
private final IEnergyStorage energy = new EnergyStorage(MAX_ENERGY);
|
|
|
|
@Override
|
|
public @Nullable IEnergyStorage getCapability(@NotNull ChargingStationBlockEntity object, Direction context) {
|
|
return energy;
|
|
}
|
|
}
|
|
|
|
|
|
public static class ItemHandlerProvider implements ICapabilityProvider<ChargingStationBlockEntity, Direction, IItemHandler> {
|
|
|
|
private IItemHandler[] itemHandlers;
|
|
|
|
@Override
|
|
public @Nullable IItemHandler getCapability(@NotNull ChargingStationBlockEntity object, Direction context) {
|
|
if (context == null || object.isRemoved()) return null;
|
|
|
|
if (itemHandlers == null) {
|
|
this.itemHandlers = new IItemHandler[]{
|
|
new SidedInvWrapper(object, Direction.UP),
|
|
new SidedInvWrapper(object, Direction.DOWN),
|
|
new SidedInvWrapper(object, Direction.NORTH),
|
|
};
|
|
}
|
|
|
|
return switch (context) {
|
|
case UP -> itemHandlers[0];
|
|
case DOWN -> itemHandlers[1];
|
|
default -> itemHandlers[2];
|
|
};
|
|
}
|
|
}
|
|
|
|
}
|