/* * This file is part of Matter Overdrive * Copyright (c) 2015., Simeon Radivoev, All rights reserved. * * Matter Overdrive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Matter Overdrive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>. */ package matteroverdrive.machines.transporter; import cpw.mods.fml.common.Optional; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Context; import li.cil.oc.api.network.ManagedPeripheral; import li.cil.oc.api.network.SimpleComponent; import matteroverdrive.MatterOverdrive; import matteroverdrive.api.events.MOEventTransport; import matteroverdrive.api.inventory.UpgradeTypes; import matteroverdrive.api.transport.ITransportList; import matteroverdrive.api.transport.TransportLocation; import matteroverdrive.compat.modules.waila.IWailaBodyProvider; import matteroverdrive.data.Inventory; import matteroverdrive.data.inventory.TeleportFlashDriveSlot; import matteroverdrive.fx.ReplicatorParticle; import matteroverdrive.machines.MachineNBTCategory; import matteroverdrive.machines.transporter.components.ComponentComputers; import matteroverdrive.network.packet.client.PacketSyncTransportProgress; import matteroverdrive.tile.MOTileEntityMachineMatter; import matteroverdrive.util.math.MOMathHelper; import mcp.mobius.waila.api.IWailaConfigHandler; import mcp.mobius.waila.api.IWailaDataAccessor; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import org.lwjgl.util.vector.Vector3f; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; /** * Created by Simeon on 5/3/2015. */ @Optional.InterfaceList({ @Optional.Interface(modid = "ComputerCraft", iface = "dan200.computercraft.api.peripheral.IPeripheral"), @Optional.Interface(modid = "OpenComputers", iface = "li.cil.oc.api.network.SimpleComponent"), @Optional.Interface(modid = "OpenComputers", iface = "li.cil.oc.api.network.ManagedPeripheral") }) public class TileEntityMachineTransporter extends MOTileEntityMachineMatter implements ITransportList, IWailaBodyProvider, IPeripheral, SimpleComponent, ManagedPeripheral { public static final int MAX_ENTETIES_PRE_TRANSPORT = 3; public static final int TRANSPORT_TIME = 70; public static final int TRANSPORT_DELAY = 80; private static final int TRANSPORT_RANGE = 32; public static final int ENERGY_STORAGE = 1024000; public static final int MAX_ENERGY_EXTRACT = 32000; public static final int ENERGY_PER_UNIT = 16; public List<TransportLocation> locations; public int selectedLocation; public int usbSlotID; int transportTimer; long transportTracker; private ComponentComputers computerComponent; public TileEntityMachineTransporter() { super(5); energyStorage.setCapacity(ENERGY_STORAGE); energyStorage.setMaxExtract(MAX_ENERGY_EXTRACT); matterStorage.setCapacity(512); locations = new ArrayList<>(); selectedLocation = 0; playerSlotsHotbar = true; } @Override protected void RegisterSlots(Inventory inventory) { super.RegisterSlots(inventory); usbSlotID = inventory.AddSlot(new TeleportFlashDriveSlot(true)); } @Override public void writeCustomNBT(NBTTagCompound nbt, EnumSet<MachineNBTCategory> categories, boolean toDisk) { super.writeCustomNBT(nbt, categories, toDisk); if (categories.contains(MachineNBTCategory.CONFIGS)) { writeLocations(nbt); } } @Override public void readCustomNBT(NBTTagCompound nbt, EnumSet<MachineNBTCategory> categories) { super.readCustomNBT(nbt, categories); if (categories.contains(MachineNBTCategory.CONFIGS)) { readLocations(nbt); } } @Override protected void onAwake(Side side) { } public void readLocations(NBTTagCompound nbt) { locations.clear(); NBTTagList locationsList = nbt.getTagList("transportLocations",10); for (int i = 0;i < locationsList.tagCount();i++) { locations.add(new TransportLocation(locationsList.getCompoundTagAt(i))); } selectedLocation = nbt.getInteger("selectedTransport"); } public void writeLocations(NBTTagCompound nbt) { NBTTagList locationsList = new NBTTagList(); for (TransportLocation location : locations) { NBTTagCompound positionTag = new NBTTagCompound(); location.writeToNBT(positionTag); locationsList.appendTag(positionTag); } nbt.setTag("transportLocations", locationsList); nbt.setInteger("selectedTransport", selectedLocation); } @Override public void onAdded(World world, int x, int y, int z) { } @Override public void onPlaced(World world, EntityLivingBase entityLiving) { } @Override public void onDestroyed() { } @Override public void writeToDropItem(ItemStack itemStack) { super.writeToDropItem(itemStack); if (!itemStack.hasTagCompound()) itemStack.setTagCompound(new NBTTagCompound()); writeLocations(itemStack.getTagCompound()); } @Override public void readFromPlaceItem(ItemStack itemStack) { super.readFromPlaceItem(itemStack); if (!itemStack.hasTagCompound()) itemStack.setTagCompound(new NBTTagCompound()); readLocations(itemStack.getTagCompound()); } @Override protected void onActiveChange() { } @Override public void updateEntity() { super.updateEntity(); manageTeleportation(); } @Override protected void registerComponents() { super.registerComponents(); computerComponent = new ComponentComputers(this); addComponent(computerComponent); } void manageTeleportation() { List<Entity> entities = worldObj.getEntitiesWithinAABB(Entity.class, AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 2, zCoord + 1)); TransportLocation position = getSelectedLocation(); if (!worldObj.isRemote) { if (getEnergyStorage().getEnergyStored() > getEnergyDrain() && entities.size() > 0 && isLocationValid(getSelectedLocation())) { if (transportTracker < worldObj.getTotalWorldTime()) { transportTimer++; if (transportTimer >= getSpeed()) { for (int i = 0;i < Math.min(entities.size(),MAX_ENTETIES_PRE_TRANSPORT);i++) { Teleport(entities.get(i),position); transportTracker = worldObj.getTotalWorldTime() + getTransportDelay(); } energyStorage.modifyEnergyStored(-getEnergyDrain()); transportTimer = 0; MatterOverdrive.packetPipeline.sendToDimention(new PacketSyncTransportProgress(this),worldObj); } else { MatterOverdrive.packetPipeline.sendToAllAround(new PacketSyncTransportProgress(this), this, TRANSPORT_RANGE); } } } else { if (transportTimer != 0) { transportTimer = 0; MatterOverdrive.packetPipeline.sendToDimention(new PacketSyncTransportProgress(this),worldObj); } } } else { if (transportTimer > 0) { for (Entity entity : entities) { SpawnReplicateParticles(entity,new Vector3f((float)entity.posX,yCoord,(float)entity.posZ)); } for (Entity entity : entities) { SpawnReplicateParticles(entity,new Vector3f(position.x,position.y-1,position.z)); } } } } public void Teleport(Entity entity,TransportLocation position) { if(!MinecraftForge.EVENT_BUS.post(new MOEventTransport(new ChunkCoordinates(xCoord,yCoord,zCoord),position,entity))) { if (entity instanceof EntityLivingBase) { ((EntityLivingBase) entity).setPositionAndUpdate(position.x, position.y, position.z); } else { entity.setPosition(position.x, position.y, position.z); } } } public TransportLocation getSelectedLocation() { if (selectedLocation < locations.size() && selectedLocation >= 0) { TransportLocation location = locations.get(selectedLocation); int range = getTransportRange(); //location.x = MathHelper.clampI(location.x,xCoord - range,xCoord + range); //location.y = MathHelper.clampI(location.y,yCoord - range,yCoord + range); //location.z = MathHelper.clampI(location.z,zCoord - range,zCoord + range); return location; } return new TransportLocation(xCoord,yCoord,zCoord,"Unknown"); } public boolean isLocationValid(TransportLocation location) { return !(location.x == xCoord && location.y < yCoord + 4 && location.y > yCoord - 4 && location.z == zCoord) && location.getDistance(xCoord,yCoord,zCoord) < getTransportRange(); } public void setSelectedLocation(int x,int y,int z,String name) { if (selectedLocation < locations.size() && selectedLocation >= 0) { TransportLocation location = locations.get(selectedLocation); if (location != null) { location.setPosition(x,y,z); location.setName(name); }else { locations.set(selectedLocation,new TransportLocation(x,y,z,name)); } }else { selectedLocation = 0; locations.add(new TransportLocation(x,y,z,name)); } } public void addNewLocation(int x,int y,int z,String name) { locations.add(new TransportLocation(x,y,z,name)); } public void removeLocation(int at) { if (at < locations.size() && at >= 0) { locations.remove(at); selectedLocation = net.minecraft.util.MathHelper.clamp_int(selectedLocation,0,locations.size()-1); } } @SideOnly(Side.CLIENT) public void SpawnReplicateParticles(Entity entity,Vector3f p) { double entityRadius = entity.width; double entityArea = Math.max(entityRadius * entity.height,0.3); double radiusX = entityRadius + random.nextDouble() * 0.2f; double radiusZ = entityRadius + random.nextDouble() * 0.2f; double time = Math.min((double) (transportTimer) / (double) (getTransportDelay()), 1); double gravity = 0.015f; int age = (int)Math.round(MOMathHelper.easeIn(time, 5, 15, 1)); int count = (int)Math.round(MOMathHelper.easeIn(time, 2, entityArea * 15, 1)); for(int i = 0;i < count;i++) { float speed = random.nextFloat() * 0.05f + 0.15f; float height = p.y + 1 + random.nextFloat() * entity.height; Vector3f origin = new Vector3f(p.x ,height, p.z); Vector3f pos = MOMathHelper.randomSpherePoint(origin.x,origin.y,origin.z, Vec3.createVectorHelper(radiusX, 0,radiusZ), random); Vector3f dir = Vector3f.cross(Vector3f.sub(origin, pos,null), new Vector3f(0,1,0),null); dir.scale(speed); ReplicatorParticle replicatorParticle = new ReplicatorParticle(this.worldObj,pos.x,pos.y ,pos.z,dir.x,dir.y,dir.z); replicatorParticle.setCenter(origin.x,origin.y,origin.z); replicatorParticle.setParticleAge(age); replicatorParticle.setPointGravityScale(gravity); Minecraft.getMinecraft().effectRenderer.addEffect(replicatorParticle); } } public int getEnergyDrain() { TransportLocation location = getSelectedLocation(); return (int)Math.round(getUpgradeMultiply(UpgradeTypes.PowerUsage) * (location.getDistance(xCoord, yCoord, zCoord) * ENERGY_PER_UNIT)); } private int getSpeed() { return (int)Math.round(getUpgradeMultiply(UpgradeTypes.Speed) * TRANSPORT_TIME); } private int getTransportDelay() { return (int)Math.round(getUpgradeMultiply(UpgradeTypes.Speed) * TRANSPORT_DELAY); } public int getTransportRange() { return (int)Math.round(getUpgradeMultiply(UpgradeTypes.Range) * TRANSPORT_RANGE); } @Override public String getSound() { return "transporter"; } @Override public boolean hasSound() { return true; } @Override public boolean getServerActive() { return transportTimer > 0; } @Override public float soundVolume() { return 0.5f; } @Override public boolean canFill(ForgeDirection from, Fluid fluid) { return from != ForgeDirection.UP && super.canFill(from,fluid); } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { return from != ForgeDirection.UP && super.canDrain(from,fluid); } @Override public boolean isAffectedByUpgrade(UpgradeTypes type) { return type == UpgradeTypes.PowerUsage || type == UpgradeTypes.Speed || type == UpgradeTypes.Range || type == UpgradeTypes.PowerStorage; } public void setTransportTime(int time) { transportTimer = time; } public int getTransportTime() { return transportTimer; } @Override public List<TransportLocation> getPositions() { return locations; } //region WAILA @Override @Optional.Method(modid = "Waila") public List<String> getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { TileEntity te = accessor.getTileEntity(); if (te instanceof TileEntityMachineTransporter) { TileEntityMachineTransporter transporter = (TileEntityMachineTransporter)te; TransportLocation location = transporter.getSelectedLocation(); currenttip.add(String.format("%sSelected Location: %s%s", EnumChatFormatting.YELLOW, EnumChatFormatting.WHITE, location.name)); currenttip.add(String.format("%sDestination Coords: %s X:%d Y:%d Z:%d", EnumChatFormatting.YELLOW, EnumChatFormatting.WHITE, location.x, location.y, location.z)); } else { throw new RuntimeException("Transporter WAILA provider is being used for something that is not a Transporter: " + te.getClass()); } return currenttip; } //endregion //region All Computers //region ComputerCraft @Override @Optional.Method(modid = "ComputerCraft") public String getType() { return computerComponent.getType(); } @Override @Optional.Method(modid = "ComputerCraft") public String[] getMethodNames() { return computerComponent.getMethodNames(); } @Override @Optional.Method(modid = "ComputerCraft") public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException { return computerComponent.callMethod(computer,context,method,arguments); } @Override @Optional.Method(modid = "ComputerCraft") public void attach(IComputerAccess computer) { computerComponent.attach(computer); } @Override @Optional.Method(modid = "ComputerCraft") public void detach(IComputerAccess computer) { computerComponent.attach(computer); } @Override @Optional.Method(modid = "ComputerCraft") public boolean equals(IPeripheral other) { // Does this mean if it's the same type or if they're the same one? return computerComponent.equals(other); } //endregion //region Open Computers @Override @Optional.Method(modid = "OpenComputers") public String getComponentName() { return computerComponent.getComponentName(); } @Override @Optional.Method(modid = "OpenComputers") public String[] methods() { return computerComponent.methods(); } @Override @Optional.Method(modid = "OpenComputers") public Object[] invoke(String method, Context context, Arguments args) throws Exception { return computerComponent.invoke(method,context,args); } //endregion //endregion }