package com.amadornes.framez.movement;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import uk.co.qmunity.lib.misc.Pair;
import uk.co.qmunity.lib.vec.Vec3i;
import com.amadornes.framez.api.Priority;
import com.amadornes.framez.api.Priority.PriorityEnum;
import com.amadornes.framez.api.movement.BlockMovementType;
import com.amadornes.framez.api.movement.IMovable;
import com.amadornes.framez.api.movement.IMovement;
import com.amadornes.framez.api.movement.IStickinessHandler;
import com.amadornes.framez.api.movement.ISticky;
import com.amadornes.framez.init.FramezBlocks;
import com.amadornes.framez.tile.TileMoving;
import com.amadornes.framez.util.Graph;
public class MovementHelper {
public static Pair<List<MovingBlock>, List<Vec3i>> findMovedBlocks(World world, int x, int y, int z, ForgeDirection side,
IMovement movement) {
return findMovedBlocks(world, x, y, z, side, movement, new ArrayList<Vec3i>());
}
public static Pair<List<MovingBlock>, List<Vec3i>> findMovedBlocks(World world, int x, int y, int z, ForgeDirection side,
IMovement movement, List<Vec3i> ignored) {
List<MovingBlock> moved = new ArrayList<MovingBlock>();
Graph<MovingBlock> graph = new Graph<MovingBlock>();
MovingBlock firstBlock = findBlock(world, x, y, z, side, movement);
if (firstBlock == null)
return new Pair<List<MovingBlock>, List<Vec3i>>(moved, new ArrayList<Vec3i>());
moved.add(firstBlock);
List<MovingBlock> current = new ArrayList<MovingBlock>();
List<MovingBlock> tmp = new ArrayList<MovingBlock>();
current.add(firstBlock);
while (current.size() > 0) {
for (MovingBlock b : current) {
for (MovingBlock bl : findBlocks(b.getWorld(), b.getX(), b.getY(), b.getZ(), movement, moved, ignored)) {
bl.snapshot();
graph.addEdge(b, bl);
if (!tmp.contains(bl) && !current.contains(bl) && !moved.contains(bl)) {
tmp.add(bl);
moved.add(bl);
}
}
}
current.clear();
current.addAll(tmp);
tmp.clear();
}
current.clear();
Graph<MovingBlock> mst = graph.getMST(firstBlock);
List<MovingBlock> actuallyMoved = new ArrayList<MovingBlock>();
for (MovingBlock b : mst.getVertices())
if (!actuallyMoved.contains(b))
actuallyMoved.add(b);
List<MovingBlock> notMovedBlocks = new ArrayList<MovingBlock>(moved);
notMovedBlocks.removeAll(actuallyMoved);
List<Vec3i> notMoved = new ArrayList<Vec3i>();
notMoved.addAll(getBlocksInTheWay(moved, movement));
for (MovingBlock b : notMovedBlocks)
if (!notMoved.contains(movement.transform(new Vec3i(b))))
notMoved.add(new Vec3i(b));
notMovedBlocks.clear();
moved.clear();
return new Pair<List<MovingBlock>, List<Vec3i>>(actuallyMoved, notMoved);
}
public static MovingBlock findBlock(World world, int x, int y, int z, ForgeDirection side, IMovement movement) {
List<IMovable> movableList = FrameMovementRegistry.instance().findMovables(world, x, y, z);
boolean canMove = movableList.size() == 0;
for (IMovable movable : movableList) {
Priority p = null;
try {
p = movable.getClass()
.getMethod("getMovementType", World.class, int.class, int.class, int.class, ForgeDirection.class, IMovement.class)
.getAnnotation(Priority.class);
} catch (Exception e) {
e.printStackTrace();
}
if (p != null && p.value() == PriorityEnum.OVERRIDE) {
BlockMovementType type = movable.getMovementType(world, x, y, z, side, movement);
if (type != null) {
if (!type.isMovable())
return null;
return new MovingBlock(new Vec3i(x, y, z, world), null, FrameMovementRegistry.instance().findFrames(world, x, y, z));
}
}
BlockMovementType type = movable.getMovementType(world, x, y, z, side, movement);
if (type != null)
canMove |= type.isMovable();
}
if (!canMove)
return null;
return new MovingBlock(new Vec3i(x, y, z, world), null, FrameMovementRegistry.instance().findFrames(world, x, y, z));
}
public static List<MovingBlock> findBlocks(World world, int x, int y, int z, IMovement movement, List<MovingBlock> alreadyMoving,
List<Vec3i> ignored) {
List<MovingBlock> blocks = new ArrayList<MovingBlock>();
List<ISticky> stickies = FrameMovementRegistry.instance().findStickies(world, x, y, z);
for (ISticky sticky : stickies) {
for (ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
boolean handled = false;
boolean isSticky = false;
for (IStickinessHandler h : FrameMovementRegistry.instance().getStickinessHandlers()) {
if (h.canHandle(world, x, y, z, d)) {
handled = true;
isSticky = h.isSideSticky(world, x, y, z, d, movement);
break;
}
}
if (handled && !isSticky)
continue;
if (!handled && !sticky.isSideSticky(world, x, y, z, d, movement))
continue;
MovingBlock b = findBlock(world, x + d.offsetX, y + d.offsetY, z + d.offsetZ, d.getOpposite(), movement);
if (b != null && !blocks.contains(b)) {
boolean ignore = false;
for (Vec3i v : ignored) {
if (b.equals(v)) {
ignore = true;
break;
}
}
if (!ignore)
blocks.add(b);
}
}
}
return blocks;
}
public static List<Vec3i> getBlocksInTheWay(List<MovingBlock> movingBlocks, IMovement movement) {
List<Vec3i> blocks = new ArrayList<Vec3i>();
for (MovingBlock b : movingBlocks)
blocks.add(new Vec3i(b));
List<Vec3i> moved = new ArrayList<Vec3i>();
for (Vec3i v : blocks)
moved.add(movement.transform(v));
moved.removeAll(blocks);
List<Vec3i> blocking = new ArrayList<Vec3i>();
for (Vec3i v : moved) {
if (v.getBlock() == FramezBlocks.moving) {
TileEntity te = v.getTileEntity();
// Skip moving blocks, they'll disappear before the structure starts moving
if (te == null
|| (te instanceof TileMoving && (((TileMoving) te).getBlockA() == null || ((TileMoving) te).getBlockB() == null)))
continue;
}
List<IMovable> movableList = FrameMovementRegistry.instance().findMovables(v.getWorld(), v.getX(), v.getY(), v.getZ());
for (IMovable movable : movableList) {
Priority p = null;
try {
p = movable
.getClass()
.getMethod("getMovementType", World.class, int.class, int.class, int.class, ForgeDirection.class,
IMovement.class).getAnnotation(Priority.class);
} catch (Exception e) {
e.printStackTrace();
}
BlockMovementType type = movable.getMovementType(v.getWorld(), v.getX(), v.getY(), v.getZ(), ForgeDirection.UNKNOWN,
movement);
if (type != null) {
if (!type.isReplaceable()) {
blocking.add(v);
break;
}
if (p != null && p.value() == PriorityEnum.OVERRIDE)
break;
}
}
}
return blocking;
}
}