package com.thebrokenrail.energonrelics.block.entity.shifter; import com.thebrokenrail.energonrelics.EnergonRelics; import com.thebrokenrail.energonrelics.api.block.entity.core.EnergyReceiverBlockEntity; import com.thebrokenrail.energonrelics.api.energy.Action; import com.thebrokenrail.energonrelics.api.energy.tick.EnergyTickable; import com.thebrokenrail.energonrelics.api.energy.tick.EnergyTicker; import com.thebrokenrail.energonrelics.api.item.MultimeterExtra; import com.thebrokenrail.energonrelics.block.PhaseShifterBlock; import com.thebrokenrail.energonrelics.block.entity.reactor.ReactorInputBlockEntity; import com.thebrokenrail.energonrelics.config.HardcodedConfig; import com.thebrokenrail.energonrelics.util.WeightedList; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventories; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.text.LiteralText; import net.minecraft.text.MutableText; import net.minecraft.text.TranslatableText; import net.minecraft.util.Formatting; import net.minecraft.util.collection.DefaultedList; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; public class PhaseShifterBlockEntity extends EnergyReceiverBlockEntity implements Inventory, MultimeterExtra { private final static int INVENTORY_SIZE = ReactorInputBlockEntity.INVENTORY_SIZE; private final DefaultedList inv = DefaultedList.ofSize(INVENTORY_SIZE, ItemStack.EMPTY); private int cooldown = 0; public PhaseShifterBlockEntity(BlockEntityType type) { super(type); } @Override public int size() { return inv.size(); } @Override public boolean isEmpty() { boolean empty = false; for (ItemStack stack : inv) { if (stack.isEmpty()) { empty = true; break; } } return empty; } @Override public ItemStack getStack(int slot) { return inv.get(slot); } @Override public ItemStack removeStack(int slot, int amount) { ItemStack stack = Inventories.splitStack(inv, slot, amount); if (!stack.isEmpty()) { markDirty(); } return stack; } @Override public ItemStack removeStack(int slot) { return Inventories.removeStack(inv, slot); } @Override public void setStack(int slot, ItemStack stack) { inv.set(slot, stack); if (stack.getCount() > getMaxCountPerStack()) { stack.setCount(getMaxCountPerStack()); } } @Override public boolean canPlayerUse(PlayerEntity player) { if (Objects.requireNonNull(getWorld()).getBlockEntity(getPos()) != this) { return false; } else { return player.squaredDistanceTo((double) getPos().getX() + 0.5D, (double) getPos().getY() + 0.5D, (double) getPos().getZ() + 0.5D) <= 64.0D; } } @Override public void clear() { inv.clear(); } private final List phasedItems = new ArrayList<>(); @Override public void fromTag(BlockState state, CompoundTag tag) { super.fromTag(state, tag); inv.clear(); Inventories.fromTag(tag, inv); cooldown = tag.getInt("Cooldown"); phasedItems.clear(); Tag list = tag.get("PhasedItems"); if (list instanceof ListTag) { ListTag itemsTag = (ListTag) list; for (int i = 0; i < itemsTag.size(); i++) { phasedItems.add(PhasedItem.fromTag(itemsTag.getCompound(i))); } } } private int canInsert(ItemStack stack) { if (stack.getCount() <= getMaxCountPerStack()) { for (int slot = 0; slot < size(); slot++) { ItemStack oldStack = getStack(slot); if (oldStack.isItemEqual(stack)) { int newCount = oldStack.getCount() + stack.getCount(); if (newCount <= Math.min(oldStack.getMaxCount(), getMaxCountPerStack())) { return slot; } } } for (int slot = 0; slot < size(); slot++) { ItemStack oldStack = getStack(slot); if (oldStack.isEmpty()) { return slot; } } } return -1; } private long getCost() { return getCachedState().get(PhaseShifterBlock.IS_OUTPUT) ? HardcodedConfig.PHASE_SHIFTER_OUTPUT_ENERGY_REQUIRED : HardcodedConfig.PHASE_SHIFTER_INPUT_ENERGY_REQUIRED_BASE + (HardcodedConfig.PHASE_SHIFTER_INPUT_ENERGY_REQUIRED_PER_ITEM * phasedItems.size()); } @Override protected void energyTick() { assert getWorld() != null; addAction(Action.createBlockStatePropertyAction(getCost(), PhaseShifterBlock.POWERED, true, false)); boolean dirty = false; if (cooldown > 0) { cooldown--; dirty = true; } if (getCachedState().get(PhaseShifterBlock.POWERED) && !getCachedState().get(PhaseShifterBlock.IS_OUTPUT)) { List outputs = new ArrayList<>(); for (EnergyTickable tickable : EnergyTicker.getAllLoaded()) { if (tickable instanceof PhaseShifterBlockEntity && ((PhaseShifterBlockEntity) tickable).getCachedState().get(PhaseShifterBlock.IS_OUTPUT) && Objects.requireNonNull(((PhaseShifterBlockEntity) tickable).getWorld()).getRegistryKey() == getWorld().getRegistryKey() && ((PhaseShifterBlockEntity) tickable).cooldown == 0 && ((PhaseShifterBlockEntity) tickable).getCachedState().get(PhaseShifterBlock.COLOR) == getCachedState().get(PhaseShifterBlock.COLOR)) { outputs.add((PhaseShifterBlockEntity) tickable); } } if (phasedItems.size() > 0) { dirty = true; } Iterator iterator = phasedItems.iterator(); while (iterator.hasNext()) { PhasedItem item = iterator.next(); if (item.cooldown > 0) { item.cooldown--; } else { Map validOutputs = new HashMap<>(); for (PhaseShifterBlockEntity output : outputs) { int slot = output.canInsert(item.item); if (slot != -1 && output.getPos().isWithinDistance(getPos(), item.getRange())) { validOutputs.put(output, slot); } } boolean success = validOutputs.size() > 0 && HardcodedConfig.PHASE_SHIFTER_SUCCESS_CHANCE >= getWorld().random.nextFloat(); if (success) { WeightedList weightedList = new WeightedList<>(); for (PhaseShifterBlockEntity output : validOutputs.keySet()) { weightedList.add(1, output); } PhaseShifterBlockEntity output = weightedList.pick(getWorld().random); int slot = validOutputs.get(output); ItemStack stack = item.item.copy(); stack.increment(output.getStack(slot).getCount()); output.setStack(slot, stack); output.resetCooldown(); output.markDirty(); iterator.remove(); } else { item.roll++; item.cooldown = HardcodedConfig.PHASE_SHIFTER_INPUT_ROLL_COOLDOWN; } } } if (cooldown == 0) { ItemStack next = ItemStack.EMPTY; for (int slot = 0; slot < size(); slot++) { ItemStack stack = getStack(slot); if (!stack.isEmpty()) { next = stack.split(1); } } if (!next.isEmpty()) { phasedItems.add(new PhasedItem(next)); resetCooldown(); dirty = true; } } } else if (phasedItems.size() > 0) { phasedItems.clear(); dirty = true; } if (dirty) { markDirty(); } } private void resetCooldown() { cooldown = getCachedState().get(PhaseShifterBlock.IS_OUTPUT) ? HardcodedConfig.PHASE_SHIFTER_OUTPUT_COOLDOWN : HardcodedConfig.PHASE_SHIFTER_INPUT_COOLDOWN; } @Override public CompoundTag toTag(CompoundTag tag) { super.toTag(tag); Inventories.toTag(tag, inv); tag.putInt("Cooldown", cooldown); ListTag itemsTag = new ListTag(); for (PhasedItem item : phasedItems) { itemsTag.add(item.toTag()); } tag.put("PhasedItems", itemsTag); return tag; } @Override public MutableText getExtra() { return getCachedState().get(PhaseShifterBlock.IS_OUTPUT) ? null : new TranslatableText("text." + EnergonRelics.NAMESPACE + ".phase_shifter_extra", new LiteralText(String.valueOf(phasedItems.size())).formatted(Formatting.WHITE)); } }