RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/block/entity/interaction/DeepDrillEntity.java
branch: 1.21
back to repo
package rearth.oritech.block.entity.interaction;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.inventory.Inventories;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import rearth.oritech.Oritech;
import rearth.oritech.api.energy.EnergyApi;
import rearth.oritech.api.energy.containers.DynamicEnergyStorage;
import rearth.oritech.api.item.ItemApi;
import rearth.oritech.api.item.containers.SimpleInventoryStorage;
import rearth.oritech.client.init.ParticleContent;
import rearth.oritech.init.BlockEntitiesContent;
import rearth.oritech.init.TagContent;
import rearth.oritech.init.recipes.RecipeContent;
import rearth.oritech.network.NetworkContent;
import rearth.oritech.util.AutoPlayingSoundKeyframeHandler;
import rearth.oritech.util.Geometry;
import rearth.oritech.util.MultiblockMachineController;
import rearth.oritech.util.SimpleCraftingInventory;
import software.bernie.geckolib.animatable.GeoBlockEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.util.GeckoLibUtil;

import java.util.ArrayList;
import java.util.List;

import static rearth.oritech.block.base.block.MultiblockMachine.ASSEMBLED;
import static rearth.oritech.block.base.entity.MachineBlockEntity.*;

public class DeepDrillEntity extends BlockEntity implements BlockEntityTicker<DeepDrillEntity>, EnergyApi.BlockProvider, GeoBlockEntity, ItemApi.BlockProvider, MultiblockMachineController {
    
    // work data
    private boolean initialized;
    private final List<Block> targetedOre = new ArrayList<>();
    private int progress;
    private long lastWorkTime;
    private boolean networkDirty;
    
    // config
    private int worktime = Oritech.CONFIG.deepDrillConfig.stepsPerOre();
    private int energyPerStep = Oritech.CONFIG.deepDrillConfig.energyPerStep();
    
    // storage
    protected final DynamicEnergyStorage energyStorage = new DynamicEnergyStorage(Oritech.CONFIG.deepDrillConfig.energyCapacity(), 0, 0, this::markDirty);
    
    public final SimpleInventoryStorage inventory = new SimpleInventoryStorage(1, this::markDirty);
    
    // multiblock
    private final ArrayList<BlockPos> coreBlocksConnected = new ArrayList<>();
    private float coreQuality = 1f;
    
    // animation
    protected final AnimatableInstanceCache animatableInstanceCache = GeckoLibUtil.createInstanceCache(this);
    private final AnimationController<DeepDrillEntity> animationController = getAnimationController();
    
    public DeepDrillEntity(BlockPos pos, BlockState state) {
        super(BlockEntitiesContent.DEEP_DRILL_ENTITY, pos, state);
    }
    
    public boolean init(boolean manual) {
        
        initialized = true;
        targetedOre.clear();
        loadOreBlocks(manual);

        return !targetedOre.isEmpty();
    }
    
    @Override
    public void tick(World world, BlockPos pos, BlockState state, DeepDrillEntity blockEntity) {
        if (world.isClient) return;
        
        if (isActive(state) && !initialized && (world.getTime() + pos.asLong()) % 60 == 0) {
            init(false);
        }
        
        if (world.isClient() || !initialized || targetedOre.isEmpty()) return;
        if (!inventory.isEmpty() && inventory.heldStacks.get(0).getCount() >= inventory.heldStacks.get(0).getMaxCount())
            return;    // inv full
        
        if (energyStorage.amount >= energyPerStep) {
            progress++;
            energyStorage.amount -= energyPerStep;
            lastWorkTime = world.getTime();
            networkDirty = true;
            
            var particlePos = getCenter(0);
            ParticleContent.FURNACE_BURNING.spawn(world, Vec3d.of(particlePos), 1);
        }
        
        // try increasing faster if too much energy is provided
        for (int i = 0; i < 5; i++) {
            if (energyStorage.amount >= energyPerStep) {
                progress++;
                energyStorage.amount -= energyPerStep;
            }
        }
        
        if (progress >= worktime) {
            craftResult(world, pos);
            progress -= worktime;
            this.markDirty();
        }
        
        updateNetwork();
        
    }
    
    private BlockPos getCenter(int y) {
        var state = getCachedState();
        var facing = state.get(Properties.HORIZONTAL_FACING);
        return pos.add(Geometry.rotatePosition(new Vec3i(1, y, 0), facing));
    }
    
    private void loadOreBlocks(boolean manual) {
        var center = getCenter(-1);
        
        for (int x = -1; x <= 1; x++) {
            for (int z = -1; z <= 1; z++) {
                // Only target the top-most uncovered resource node
                for (int y = 0; y >= -2; y--) {
                    var target = center.add(x, y, z);
                    var targetState = world.getBlockState(target);
                    if (targetState.isIn(TagContent.RESOURCE_NODES)) {
                        if (manual) ParticleContent.DEBUG_BLOCK.spawn(world, Vec3d.of(target));
                        targetedOre.add(targetState.getBlock());
                        break;
                    } else if (!targetState.isAir()) break;
                }
            }
        }  
    }
    
    private void updateNetwork() {
        if (networkDirty && world.getTime() % 5 == 0) {
            NetworkContent.MACHINE_CHANNEL.serverHandle(this).send(new NetworkContent.DeepDrillSyncPacket(pos, lastWorkTime));
            networkDirty = false;
        }
    }
    
    private void craftResult(World world, BlockPos pos) {
        var usedOre = targetedOre.get(world.random.nextBetweenExclusive(0, targetedOre.size()));
        var nodeOreBlockItem = usedOre.asItem();
        var sampleInv = new SimpleCraftingInventory(new ItemStack(nodeOreBlockItem, 1));
        
        var recipeCandidate = world.getRecipeManager().getFirstMatch(RecipeContent.DEEP_DRILL, sampleInv, world);
        if (recipeCandidate.isEmpty())
            return;
        
        var output = recipeCandidate.get().value().getResults().get(0);
        inventory.insert(output, false);
    }
    
    @Override
    protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        super.writeNbt(nbt, registryLookup);
        Inventories.writeNbt(nbt, inventory.heldStacks, false, registryLookup);
        addMultiblockToNbt(nbt);
        nbt.putLong("energy_stored", energyStorage.amount);
    }
    
    @Override
    protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        super.readNbt(nbt, registryLookup);
        Inventories.readNbt(nbt, inventory.heldStacks, registryLookup);
        loadMultiblockNbtData(nbt);
        energyStorage.amount = nbt.getLong("energy_stored");
    }
    
    @Override
    public EnergyApi.EnergyStorage getEnergyStorage(Direction direction) {
        return energyStorage;
    }
    
    @Override
    public ItemApi.InventoryStorage getInventoryStorage(Direction direction) {
        return inventory;
    }
    
    @Override
    public List<Vec3i> getCorePositions() {
        return List.of(
          new Vec3i(0, 0, 1),
          new Vec3i(0, 0, -1),
          new Vec3i(-1, 0, 1),
          new Vec3i(-1, 0, 0),
          new Vec3i(-1, 0, -1),
          new Vec3i(-2, 0, 1),
          new Vec3i(-2, 0, 0),
          new Vec3i(-2, 0, -1),
          new Vec3i(0, 1, 1),
          new Vec3i(0, 1, 0),
          new Vec3i(0, 1, -1),
          new Vec3i(-1, 1, 1),
          new Vec3i(-1, 1, 0),
          new Vec3i(-1, 1, -1),
          new Vec3i(-2, 1, 1),
          new Vec3i(-2, 1, 0),
          new Vec3i(-2, 1, -1),
          new Vec3i(0, 2, 1),
          new Vec3i(0, 2, 0),
          new Vec3i(0, 2, -1),
          new Vec3i(-1, 2, 1),
          new Vec3i(-1, 2, 0),
          new Vec3i(-1, 2, -1),
          new Vec3i(-2, 2, 1),
          new Vec3i(-2, 2, 0),
          new Vec3i(-2, 2, -1)
        );
    }
    
    @Override
    public Direction getFacingForMultiblock() {
        var state = getCachedState();
        return state.get(Properties.HORIZONTAL_FACING).getOpposite();
    }
    
    @Override
    public BlockPos getPosForMultiblock() {
        return pos;
    }
    
    @Override
    public World getWorldForMultiblock() {
        return world;
    }
    
    @Override
    public ArrayList<BlockPos> getConnectedCores() {
        return coreBlocksConnected;
    }
    
    @Override
    public void setCoreQuality(float quality) {
        this.coreQuality = quality;
    }
    
    @Override
    public float getCoreQuality() {
        return coreQuality;
    }
    
    @Override
    public ItemApi.InventoryStorage getInventoryForMultiblock() {
        return inventory;
    }
    
    @Override
    public EnergyApi.EnergyStorage getEnergyStorageForMultiblock(Direction direction) {
        return null;
    }
    
    @Override
    public void playSetupAnimation() {
        animationController.setAnimation(SETUP);
        animationController.forceAnimationReset();
    }
    
    @Override
    public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
        controllers.add(animationController);
    }
    
    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return animatableInstanceCache;
    }
    
    private AnimationController<DeepDrillEntity> getAnimationController() {
        return new AnimationController<>(this, state -> {
            
            if (state.isCurrentAnimation(SETUP)) {
                if (state.getController().hasAnimationFinished()) {
                    state.setAndContinue(IDLE);
                } else {
                    return state.setAndContinue(SETUP);
                }
            }
            
            if (isActive(getCachedState())) {
                
                var idleTime = world.getTime() - lastWorkTime;
                
                if (idleTime < 60) {
                    return state.setAndContinue(WORKING);
                } else {
                    return state.setAndContinue(IDLE);
                }
            } else {
                return state.setAndContinue(PACKAGED);
            }
        }).setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>());
    }
    
    public void setLastWorkTime(long lastWorkTime) {
        this.lastWorkTime = lastWorkTime;
    }
    
    private boolean isActive(BlockState state) {
        return state.get(ASSEMBLED);
    }
}