package openblocks.integration; import com.google.common.base.Preconditions; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import java.lang.ref.WeakReference; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.ForgeDirection; import openblocks.Config; import openblocks.common.entity.EntityMagnet; import openblocks.common.entity.EntityMagnet.IEntityBlockFactory; import openblocks.common.entity.EntityMagnet.IOwner; import openmods.entity.EntityBlock; import openmods.fakeplayer.FakePlayerPool; import openmods.fakeplayer.FakePlayerPool.PlayerUserReturning; import openmods.fakeplayer.OpenModsFakePlayer; import openperipheral.api.adapter.IWorldProvider; import openperipheral.api.adapter.method.Alias; import openperipheral.api.adapter.method.Arg; import openperipheral.api.adapter.method.IMultiReturn; import openperipheral.api.adapter.method.ReturnType; import openperipheral.api.adapter.method.ScriptCallable; import openperipheral.api.architecture.IArchitectureAccess; import openperipheral.api.architecture.IAttachable; import openperipheral.api.helpers.MultiReturn; import openperipheral.api.peripheral.ExposeInterface; import openperipheral.api.peripheral.PeripheralTypeId; @PeripheralTypeId("openblocks_magnet") @ExposeInterface({ ITickingTurtle.class, IAttachable.class }) public class MagnetControlAdapter implements ITickingTurtle, IWorldProvider, IAttachable { public class Owner implements IOwner { private final Vec3 target; public Owner() { this.target = Vec3.createVectorHelper(0, 0, 0); } public synchronized void setTargetPosition(double x, double y, double z) { target.xCoord = x; target.yCoord = y; target.zCoord = z; } public synchronized Vec3 getTarget(Vec3 pos, ForgeDirection side) { pos.yCoord += target.yCoord; switch (side) { case NORTH: pos.xCoord += target.zCoord; pos.zCoord -= target.xCoord; break; case SOUTH: pos.xCoord -= target.zCoord; pos.zCoord += target.xCoord; break; case WEST: pos.xCoord -= target.xCoord; pos.zCoord -= target.zCoord; break; case EAST: pos.xCoord += target.xCoord; pos.zCoord += target.zCoord; break; default: break; } return pos.addVector(0.5, 0.5, 0.5); } @Override public boolean isValid(EntityMagnet magnet) { return turtle != null && turtle.getWorld() != null && isAttached; } @Override public Vec3 getTarget() { return getTarget(getTurtlePosition(), getTurtleFacing()); } @Override public EntityBlock createByPlayer(final IEntityBlockFactory factory) { World world = turtle.getWorld(); if (world instanceof WorldServer) return FakePlayerPool.instance.executeOnPlayer((WorldServer)world, new PlayerUserReturning<EntityBlock>() { @Override public EntityBlock usePlayer(OpenModsFakePlayer fakePlayer) { return factory.create(fakePlayer); } }); return null; } } private final TurtleSide side; private final ITurtleAccess turtle; private boolean isAttached; private WeakReference<EntityMagnet> magnet = new WeakReference<EntityMagnet>(null); private Owner magnetOwner; public MagnetControlAdapter(ITurtleAccess turtle, TurtleSide side) { this.turtle = turtle; this.side = side; } public enum SpawnSide { Left, Right; } private int fuelTick = 0; @Override public World getWorld() { return turtle.getWorld(); } protected boolean consumeFuel(int amount) { return turtle.consumeFuel(amount); } protected SpawnSide getSpawnSide() { switch (side) { case Left: return SpawnSide.Left; case Right: default: return SpawnSide.Right; } } protected ForgeDirection getTurtleFacing() { return ForgeDirection.getOrientation(turtle.getDirection()); } protected Vec3 getTurtlePosition() { ChunkCoordinates coord = turtle.getPosition(); return Vec3.createVectorHelper(coord.posX, coord.posY, coord.posZ); } @ScriptCallable(description = "Activate magnet") public void activate() { EntityMagnet magnet = this.magnet.get(); Preconditions.checkState(magnet == null || magnet.isDead, "Magnet already active"); World world = getWorld(); Preconditions.checkNotNull(world, "Trying to spawn magnet, but turtle is unloaded"); Preconditions.checkState(canSpawn(world), "Can't deploy magnet"); Preconditions.checkState(consumeFuel(5), "No fuel"); magnetOwner = new Owner(); magnetOwner.target.zCoord = getSpawnSide() == SpawnSide.Left? -1 : 1; magnet = new EntityMagnet(world, magnetOwner, true); world.spawnEntityInWorld(magnet); magnet.playSound("mob.endermen.portal", 1, 1); this.magnet = new WeakReference<EntityMagnet>(magnet); } @ScriptCallable(description = "Deactive magnet") public void deactivate() { despawnMagnet(true); } @ScriptCallable(description = "Set target for magnet") public void setTarget(@Arg(name = "x") double x, @Arg(name = "y") double y, @Arg(name = "z") double z) { Preconditions.checkNotNull(magnetOwner, "Magnet not active"); Preconditions.checkArgument(checkTargetRange(x, y, z), "Target out of range"); magnetOwner.setTargetPosition(x, y, z); } @ScriptCallable(returnTypes = { ReturnType.NUMBER, ReturnType.NUMBER, ReturnType.NUMBER }, description = "Get turtle position") public IMultiReturn getPosition() { EntityMagnet magnet = getMagnet(); Vec3 rotated = getRelativeDistance(magnet); return MultiReturn.wrap(rotated.xCoord, rotated.yCoord, rotated.zCoord); } @ScriptCallable(returnTypes = ReturnType.BOOLEAN, description = "Is magnet above grabbable entity") public boolean isAboveEntity() { return getMagnet().isAboveTarget(); } @Alias("toggle") @ScriptCallable(returnTypes = ReturnType.BOOLEAN, description = "Grab or release entity/block under magnet") public boolean toggleMagnet() { return getMagnet().toggleMagnet(); } @ScriptCallable(returnTypes = ReturnType.BOOLEAN, description = "Is magnet currently grabbing block or entity") public boolean isGrabbing() { return getMagnet().isLocked(); } @Alias("distance") @ScriptCallable(returnTypes = { ReturnType.NUMBER, ReturnType.NUMBER, ReturnType.NUMBER }) public IMultiReturn getDistanceToTarget() { EntityMagnet magnet = getMagnet(); Vec3 current = getRelativeDistance(magnet); Vec3 target = magnetOwner.target; return MultiReturn.wrap(current.xCoord - target.xCoord, current.yCoord - target.yCoord, current.zCoord - target.zCoord); } @Override public void onPeripheralTick() { EntityMagnet magnet = this.magnet.get(); if (magnet != null && !magnet.isDead && isAttached) { if (++fuelTick >= 20) { fuelTick = 0; int fuel = magnet.isLocked()? 2 : 1; if (!consumeFuel(fuel)) despawnMagnet(false); } } } private static boolean checkTargetRange(double x, double y, double z) { return Math.abs(x) <= Config.turtleMagnetRange && Math.abs(y) <= Config.turtleMagnetRange && Math.abs(z) <= Config.turtleMagnetRange; } private Vec3 getRelativeDistance(EntityMagnet magnet) { Vec3 magnetPos = Vec3.createVectorHelper(magnet.posX, magnet.posY, magnet.posZ); Vec3 turtlePos = getTurtlePosition().addVector(0.5, 0.5, 0.5); Vec3 dist = turtlePos.subtract(magnetPos); ForgeDirection side = getTurtleFacing(); switch (side) { case NORTH: return Vec3.createVectorHelper(-dist.zCoord, dist.yCoord, dist.xCoord); case SOUTH: return Vec3.createVectorHelper(dist.zCoord, dist.yCoord, -dist.xCoord); case EAST: return Vec3.createVectorHelper(dist.xCoord, dist.yCoord, dist.zCoord); case WEST: return Vec3.createVectorHelper(-dist.xCoord, dist.yCoord, -dist.zCoord); default: return dist; } } private boolean canSpawn(World world) { ForgeDirection facing = getTurtleFacing(); Vec3 position = getTurtlePosition(); SpawnSide side = getSpawnSide(); ForgeDirection spawnSide = facing.getRotation((side == SpawnSide.Left)? ForgeDirection.DOWN : ForgeDirection.UP); int x = MathHelper.floor_double(position.xCoord) + spawnSide.offsetX; int y = MathHelper.floor_double(position.yCoord) + spawnSide.offsetY; int z = MathHelper.floor_double(position.zCoord) + spawnSide.offsetZ; return world.isAirBlock(x, y, z); } private EntityMagnet getMagnet() { EntityMagnet magnet = this.magnet.get(); Preconditions.checkState(magnet != null && !magnet.isDead, "Magnet not active"); return magnet; } private void despawnMagnet(boolean checkPosition) { EntityMagnet magnet = this.magnet.get(); Preconditions.checkNotNull(magnet, "Magnet not active"); Vec3 magnetPos = Vec3.createVectorHelper(magnet.posX, magnet.posY, magnet.posZ); Vec3 turtlePos = getTurtlePosition().addVector(0.5, 0.5, 0.5); Preconditions.checkState(!checkPosition || canOperateOnMagnet(magnetPos, turtlePos), "Magnet too far"); magnet.playSound("mob.endermen.portal", 1, 1); magnet.setDead(); this.magnet.clear(); magnetOwner = null; } private static boolean canOperateOnMagnet(Vec3 magnetPos, Vec3 turtlePos) { return magnetPos.squareDistanceTo(turtlePos) <= Config.turtleMagnetRangeDeactivate * Config.turtleMagnetRangeDeactivate; } @Override public boolean isValid() { return turtle != null && turtle.getWorld() != null && isAttached; } @Override public void addComputer(IArchitectureAccess computer) { isAttached = true; } @Override public void removeComputer(IArchitectureAccess computer) { isAttached = false; } }