ConstructionWand/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java

142 lines
8.3 KiB
Java

package thetadev.constructionwand.wand.action;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.api.IWandSupplier;
import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.wand.undo.DestroySnapshot;
import thetadev.constructionwand.wand.undo.ISnapshot;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
public class ActionDestruction implements IWandAction
{
@Override
public int getLimit(ItemStack wand) {
return ConfigServer.getWandProperties(wand.getItem()).getDestruction();
}
@Nonnull
@Override
public List<ISnapshot> getSnapshots(Level world, Player player, BlockHitResult rayTraceResult,
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
LinkedList<ISnapshot> destroySnapshots = new LinkedList<>();
// Current list of block positions to process
LinkedList<BlockPos> candidates = new LinkedList<>();
// All positions that were processed (dont process blocks multiple times)
HashSet<BlockPos> allCandidates = new HashSet<>();
// Block face the wand was pointed at
Direction breakFace = rayTraceResult.getDirection();
// Block the wand was pointed at
BlockPos startingPoint = rayTraceResult.getBlockPos();
BlockState targetBlock = world.getBlockState(rayTraceResult.getBlockPos());
// Is break direction allowed by lock?
// Tried to break blocks from top/bottom face, so the wand should allow breaking in NS/EW direction
if(breakFace == Direction.UP || breakFace == Direction.DOWN) {
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) || options.testLock(WandOptions.LOCK.EASTWEST))
candidates.add(startingPoint);
}
// Tried to break blocks from side face, so the wand should allow breaking in horizontal/vertical direction
else if(options.testLock(WandOptions.LOCK.HORIZONTAL) || options.testLock(WandOptions.LOCK.VERTICAL))
candidates.add(startingPoint);
// Process current candidates, stop when none are avaiable or block limit is reached
while(!candidates.isEmpty() && destroySnapshots.size() < limit) {
BlockPos currentCandidate = candidates.removeFirst();
// Only break blocks facing the player, with no collidable blocks in between
if(!WandUtil.isBlockPermeable(world, currentCandidate.offset(breakFace.getNormal()))) continue;
try {
BlockState candidateBlock = world.getBlockState(currentCandidate);
// If target and candidate blocks match and the current candidate has not been processed
if(options.matchBlocks(targetBlock.getBlock(), candidateBlock.getBlock()) &&
allCandidates.add(currentCandidate)) {
DestroySnapshot snapshot = DestroySnapshot.get(world, player, currentCandidate);
if(snapshot == null) continue;
destroySnapshots.add(snapshot);
switch(breakFace) {
case DOWN:
case UP:
if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) {
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()));
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()));
}
if(options.testLock(WandOptions.LOCK.EASTWEST)) {
candidates.add(currentCandidate.offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.WEST.getNormal()));
}
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) && options.testLock(WandOptions.LOCK.EASTWEST)) {
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.WEST.getNormal()));
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.WEST.getNormal()));
}
break;
case NORTH:
case SOUTH:
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
candidates.add(currentCandidate.offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.WEST.getNormal()));
}
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
candidates.add(currentCandidate.offset(Direction.UP.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()));
}
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.WEST.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.EAST.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.WEST.getNormal()));
}
break;
case EAST:
case WEST:
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()));
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()));
}
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
candidates.add(currentCandidate.offset(Direction.UP.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()));
}
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.NORTH.getNormal()));
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.SOUTH.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.NORTH.getNormal()));
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.SOUTH.getNormal()));
}
break;
}
}
} catch(Exception e) {
// Can't do anything, could be anything.
// Skip if anything goes wrong.
}
}
return destroySnapshots;
}
@Nonnull
@Override
public List<ISnapshot> getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult,
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
return new ArrayList<>();
}
}