package mcjty.rftools.blocks.endergen; import cofh.api.energy.IEnergyConnection; import mcjty.lib.api.MachineInformation; import mcjty.lib.entity.GenericEnergyProviderTileEntity; import mcjty.lib.network.Argument; import mcjty.lib.network.PacketServerCommand; import mcjty.lib.varia.BlockTools; import mcjty.lib.varia.Coordinate; import mcjty.lib.varia.Logging; import mcjty.rftools.RFTools; import mcjty.rftools.network.RFToolsMessages; import mcjty.rftools.varia.EnergyTools; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Vec3; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; public class EndergenicTileEntity extends GenericEnergyProviderTileEntity implements MachineInformation { private static Random random = new Random(); public static String CMD_SETDESTINATION = "setDest"; public static String CMD_GETSTAT_RF = "getStatRF"; public static String CLIENTCMD_GETSTAT_RF = "getStatRF"; public static String CMD_GETSTAT_LOST = "getStatLost"; public static String CLIENTCMD_GETSTAT_LOST = "getStatLost"; public static String CMD_GETSTAT_LAUNCHED = "getStatLaunched"; public static String CLIENTCMD_GETSTAT_LAUNCHED = "getStatLaunched"; public static String CMD_GETSTAT_OPPORTUNITIES = "getStatOpp"; public static String CLIENTCMD_GETSTAT_OPPORTUNITIES = "getStatOpp"; private static final String[] TAGS = new String[]{"rftick", "lost", "launched", "opportunities"}; private static final String[] TAG_DESCRIPTIONS = new String[]{"Average RF/tick for the last 5 seconds", "Amount of pearls that were lost during the last 5 seconds", "Amount of pearls that were launched during the last 5 seconds", "Number of opportunities for the last 5 seconds"}; public static final int CHARGE_IDLE = 0; public static final int CHARGE_HOLDING = -1; // The current chargingMode status. // CHARGE_IDLE means this entity is doing nothing. // A positive number means it is chargingMode up from 0 to 15. When it reaches 15 it will go back to idle unless // it was hit by an endergenic pearl in the mean time. In that case it goes to 'holding' state. // CHARGE_HOLDING means this entity is holding an endergenic pearl. Whie it does that it consumes // energy. If internal energy is depleted then the endergenic pearl is lost and the mode goes // back to idle. private int chargingMode = CHARGE_IDLE; // The current age of the pearl we're holding. Will be used to calculate bonuses // for powergeneration on pearls that are in the network for a longer time. private int currentAge = 0; // The location of the destination endergenic generator. private Coordinate destination = null; private int distance = 0; // Distance between this block and destination in ticks // For pulse detection. private boolean prevIn = false; // Statistics for this generator. // These values count what is happening. private int rfGained = 0; private int rfLost = 0; private int pearlsLaunched = 0; private int pearlsLost = 0; private int pearlsOpportunities = 0; private int ticks = 100; // These values actually contain valid statistics. private int lastRfPerTick = 0; private int lastPearlsLost = 0; private int lastPearlsLaunched = 0; private int lastPearlOpportunities = 0; // Current traveling pearls. private List<EndergenicPearl> pearls = new ArrayList<EndergenicPearl>(); // This table indicates how much RF is produced when an endergenic pearl hits this block // at that specific chargingMode. private static int rfPerHit[] = new int[]{ 0, 100, 150, 200, 400, 800, 1600, 3200, 6400, 8000, 12800, 8000, 6400, 2500, 1000, 100 }; private int tickCounter = 0; // Only used for logging, counts server ticks. public EndergenicTileEntity() { super(5000000, 20000); } @Override protected void checkStateServer() { super.checkStateServer(); tickCounter++; ticks--; if (ticks < 0) { lastRfPerTick = (rfGained - rfLost) / 100; lastPearlsLost = pearlsLost; lastPearlsLaunched = pearlsLaunched; lastPearlOpportunities = pearlsOpportunities; ticks = 100; rfGained = 0; rfLost = 0; pearlsLaunched = 0; pearlsLost = 0; pearlsOpportunities = 0; } handlePearls(); handleSendingEnergy(); // First check if we're holding a pearl to see if the pearl will be lost. if (chargingMode == CHARGE_HOLDING) { if (random.nextInt(1000) <= EndergenicConfiguration.chanceLost) { // Pearl is lost. log("Server Tick: discard pearl randomly"); discardPearl(); } } int meta = worldObj.getBlockMetadata(xCoord, yCoord, zCoord); boolean newvalue = BlockTools.getRedstoneSignalIn(meta); boolean pulse = newvalue && !prevIn; prevIn = newvalue; if (pulse) { if (chargingMode == CHARGE_IDLE) { log("Server Tick: pulse -> start charging"); startCharging(); return; } else if (chargingMode == CHARGE_HOLDING) { log("Server Tick: pulse -> fire pearl"); firePearl(); return; } } if (chargingMode == CHARGE_IDLE) { // Do nothing return; } if (chargingMode == CHARGE_HOLDING) { // Consume energy to keep the endergenic pearl. int rf = EndergenicConfiguration.rfToHoldPearl; rf = (int) (rf * (4.0f - getInfusedFactor()) / 4.0f); int rfStored = getEnergyStored(ForgeDirection.DOWN); if (rfStored < rf) { // Not enough energy. Pearl is lost. log("Server Tick: insufficient energy to hold pearl (" + rfStored + " vs " + rf + ")"); discardPearl(); } else { int rfExtracted = extractEnergy(ForgeDirection.DOWN, rf, false); log("Server Tick: holding pearl, consume " + rfExtracted + " RF"); rfLost += rfExtracted; } return; } // Else we're charging up. markDirty(); chargingMode++; if (chargingMode >= 16) { log("Server Tick: charging mode ends -> idle"); chargingMode = CHARGE_IDLE; } } @Override public int getTagCount() { return 4; } @Override public String getTagName(int index) { return TAGS[index]; } @Override public String getTagDescription(int index) { return TAG_DESCRIPTIONS[index]; } @Override public String getData(int index, long millis) { switch (index) { case 0: return Integer.toString(lastRfPerTick); case 1: return Integer.toString(lastPearlsLost); case 2: return Integer.toString(lastPearlsLaunched); case 3: return Integer.toString(lastPearlOpportunities); } return null; } private void log(String message) { /* RFTools.log(worldObj, this, message);*/ } public static final ForgeDirection[] HORIZ_DIRECTIONS = {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST}; /** * Something happens, we need to notify all ender monitors. * @param mode is the new mode */ private void fireMonitors(EnderMonitorMode mode) { Coordinate pos = getCoordinate(); for (ForgeDirection dir : HORIZ_DIRECTIONS) { Coordinate c = pos.addDirection(dir); TileEntity te = worldObj.getTileEntity(c.getX(), c.getY(), c.getZ()); if (te instanceof EnderMonitorTileEntity) { int meta = worldObj.getBlockMetadata(c.getX(), c.getY(), c.getZ()); ForgeDirection k = BlockTools.getOrientationHoriz(meta); if (k == dir.getOpposite()) { EnderMonitorTileEntity enderMonitorTileEntity = (EnderMonitorTileEntity) te; enderMonitorTileEntity.fireFromEndergenic(mode); } } } } private void handleSendingEnergy() { int energyStored = getEnergyStored(ForgeDirection.DOWN); if (energyStored <= EndergenicConfiguration.keepRfInBuffer) { return; } energyStored -= EndergenicConfiguration.keepRfInBuffer; for (int i = 0 ; i < 6 ; i++) { ForgeDirection dir = ForgeDirection.getOrientation(i); int x = xCoord + dir.offsetX; int y = yCoord + dir.offsetY; int z = zCoord + dir.offsetZ; TileEntity te = worldObj.getTileEntity(x, y, z); if (EnergyTools.isEnergyTE(te)) { IEnergyConnection connection = (IEnergyConnection) te; ForgeDirection opposite = dir.getOpposite(); if (connection.canConnectEnergy(opposite)) { int rfToGive; if (EndergenicConfiguration.rfOutput <= energyStored) { rfToGive = EndergenicConfiguration.rfOutput; } else { rfToGive = energyStored; } int received = EnergyTools.receiveEnergy(te, opposite, rfToGive); energyStored -= extractEnergy(ForgeDirection.DOWN, received, false); if (energyStored <= 0) { break; } } } } } // Handle all pearls that are currently in transit. private void handlePearls() { if (pearls.isEmpty()) { return; } List<EndergenicPearl> newlist = new ArrayList<EndergenicPearl>(); for (EndergenicPearl pearl : pearls) { log("Pearls: age=" + pearl.getAge() + ", ticks left=" + pearl.getTicksLeft()); if (!pearl.handleTick(worldObj)) { // Keep the pearl. It has not arrived yet. newlist.add(pearl); } } // Replace the old list with the new one. pearls = newlist; } private void discardPearl() { spawnParticles("smoke", EndergenicConfiguration.badParticleCount); markDirty(); pearlsLost++; chargingMode = CHARGE_IDLE; fireMonitors(EnderMonitorMode.MODE_LOSTPEARL); } /** * Get the current destination. This function checks first if that destination is * still valid and if not it is reset to null (i.e. the destination was removed). * @return the destination TE or null if there is no valid one */ private EndergenicTileEntity getDestinationTE() { if (destination == null) { return null; } TileEntity te = worldObj.getTileEntity(destination.getX(), destination.getY(), destination.getZ()); if (te instanceof EndergenicTileEntity) { return (EndergenicTileEntity) te; } else { destination = null; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); return null; } } public void firePearl() { markDirty(); // This method assumes we're in holding mode. getDestinationTE(); if (destination == null) { // There is no destination so the pearl is simply lost. log("Fire Pearl: pearl lost due to lack of destination"); discardPearl(); } else { log("Fire Pearl: pearl is launched to "+destination.getX()+","+destination.getY()+","+destination.getZ()); chargingMode = CHARGE_IDLE; pearlsLaunched++; pearls.add(new EndergenicPearl(distance, destination, currentAge+1)); fireMonitors(EnderMonitorMode.MODE_PEARLFIRED); } } public void firePearlFromInjector() { markDirty(); // This method assumes we're not in holding mode. getDestinationTE(); chargingMode = CHARGE_IDLE; if (destination == null) { // There is no destination so the injected pearl is simply lost. log("Fire Pearl from injector: pearl lost due to lack of destination"); discardPearl(); } else { log("Fire Pearl from injector: pearl is launched to "+destination.getX()+","+destination.getY()+","+destination.getZ()); pearlsLaunched++; pearls.add(new EndergenicPearl(distance, destination, 0)); fireMonitors(EnderMonitorMode.MODE_PEARLFIRED); } } // This generator receives a pearl. The age of the pearl is how many times the pearl has // already generated power. public void receivePearl(int age) { fireMonitors(EnderMonitorMode.MODE_PEARLARRIVED); markDirty(); if (chargingMode == CHARGE_HOLDING) { log("Receive Pearl: pearl arrives but already holding -> both are lost"); // If this block is already holding a pearl and it still has one then both pearls are // automatically lost. discardPearl(); } else if (chargingMode == CHARGE_IDLE) { log("Receive Pearl: pearl arrives but generator is idle -> pearl is lost"); // If this block is idle and it is hit by a pearl then the pearl is lost and nothing // happens. discardPearl(); } else { // Otherwise we get RF and this block goes into holding mode. int rf = (int) (rfPerHit[chargingMode] * EndergenicConfiguration.powergenFactor); rf = (int) (rf * (getInfusedFactor() + 3.0f) / 3.0f); // Give a bonus for pearls that have been around a bit longer. int a = age*5; if (a > 100) { a = 100; } rf += rf * a / 100; // Maximum 200% bonus. Minimum no bonus. rfGained += rf; log("Receive Pearl: pearl arrives at tick " + chargingMode + ", age=" + age + ", RF=" + rf); modifyEnergyStored(rf); spawnParticles("portal", EndergenicConfiguration.goodParticleCount); chargingMode = CHARGE_HOLDING; currentAge = age; } } private void spawnParticles(String name, int amount) { if (amount <= 0) { return; } float vecX = (random.nextFloat() - 0.5F) * 0.2F; float vecY = (random.nextFloat()) * 0.1F; float vecZ = (random.nextFloat() - 0.5F) * 0.2F; ((WorldServer)worldObj).func_147487_a(name, xCoord + 0.5f, yCoord + 1.1f, zCoord + 0.5f, amount, vecX, vecY, vecZ, 0.3f); } public void startCharging() { markDirty(); chargingMode = 1; pearlsOpportunities++; } // Called from client side when a wrench is used. public void useWrench(EntityPlayer player) { Coordinate thisCoord = new Coordinate(xCoord, yCoord, zCoord); Coordinate coord = RFTools.instance.clientInfo.getSelectedTE(); TileEntity tileEntity = null; if (coord != null) { tileEntity = worldObj.getTileEntity(coord.getX(), coord.getY(), coord.getZ()); } if (!(tileEntity instanceof EndergenicTileEntity)) { // None selected. Just select this one. RFTools.instance.clientInfo.setSelectedTE(thisCoord); EndergenicTileEntity destinationTE = getDestinationTE(); if (destinationTE == null) { RFTools.instance.clientInfo.setDestinationTE(null); Logging.message(player, "Select another endergenic generator as destination"); } else { RFTools.instance.clientInfo.setDestinationTE(new Coordinate(destinationTE.xCoord, destinationTE.yCoord, destinationTE.zCoord)); int distance = getDistanceInTicks(); Logging.message(player, "Select another endergenic generator as destination (current distance " + distance + ")"); } } else if (coord.equals(thisCoord)) { // Unselect this one. RFTools.instance.clientInfo.setSelectedTE(null); RFTools.instance.clientInfo.setDestinationTE(null); } else { // Make a link. EndergenicTileEntity otherTE = (EndergenicTileEntity) tileEntity; int distance = otherTE.calculateDistance(thisCoord); if (distance >= 5) { Logging.warn(player, "Distance is too far (maximum 4)"); return; } otherTE.setDestination(thisCoord); RFTools.instance.clientInfo.setSelectedTE(null); RFTools.instance.clientInfo.setDestinationTE(null); Logging.message(player, "Destination is set (distance " + otherTE.getDistanceInTicks() + " ticks)"); } } @Override public boolean shouldRenderInPass(int pass) { return pass == 1; } public int getChargingMode() { return chargingMode; } /** * Calculate the distance in ticks between this endergenic generator and the given coordinate. * @param destination is the coordinate of the new destination * @return is the distance in ticks */ public int calculateDistance(Coordinate destination) { double d = Vec3.createVectorHelper(destination.getX(), destination.getY(), destination.getZ()).distanceTo( Vec3.createVectorHelper(xCoord, yCoord, zCoord)); return (int) (d / 3.0f) + 1; } public void setDestination(Coordinate destination) { markDirty(); this.destination = destination; distance = calculateDistance(destination); if (worldObj.isRemote) { // We're on the client. Send change to server. RFToolsMessages.INSTANCE.sendToServer(new PacketServerCommand(xCoord, yCoord, zCoord, EndergenicTileEntity.CMD_SETDESTINATION, new Argument("dest", destination))); } } public int getDistanceInTicks() { return distance; } @Override public void readFromNBT(NBTTagCompound tagCompound) { super.readFromNBT(tagCompound); chargingMode = tagCompound.getInteger("charging"); currentAge = tagCompound.getInteger("age"); destination = Coordinate.readFromNBT(tagCompound, "dest"); distance = tagCompound.getInteger("distance"); prevIn = tagCompound.getBoolean("prevIn"); pearls.clear(); NBTTagList list = tagCompound.getTagList("pearls", Constants.NBT.TAG_COMPOUND); for (int i = 0 ; i < list.tagCount() ; i++) { NBTTagCompound tc = list.getCompoundTagAt(i); EndergenicPearl pearl = new EndergenicPearl(tc); pearls.add(pearl); } } @Override public void writeToNBT(NBTTagCompound tagCompound) { super.writeToNBT(tagCompound); tagCompound.setInteger("charging", chargingMode); tagCompound.setInteger("age", currentAge); Coordinate.writeToNBT(tagCompound, "dest", destination); tagCompound.setInteger("distance", distance); tagCompound.setBoolean("prevIn", prevIn); NBTTagList pearlList = new NBTTagList(); for (EndergenicPearl pearl : pearls) { pearlList.appendTag(pearl.getTagCompound()); } tagCompound.setTag("pearls", pearlList); } @Override public boolean execute(EntityPlayerMP playerMP, String command, Map<String, Argument> args) { boolean rc = super.execute(playerMP, command, args); if (rc) { return true; } if (CMD_SETDESTINATION.equals(command)) { setDestination(args.get("dest").getCoordinate()); return true; } return false; } @Override public Integer executeWithResultInteger(String command, Map<String, Argument> args) { Integer rc = super.executeWithResultInteger(command, args); if (rc != null) { return rc; } if (CMD_GETSTAT_RF.equals(command)) { return lastRfPerTick; } else if (CMD_GETSTAT_LOST.equals(command)) { return lastPearlsLost; } else if (CMD_GETSTAT_LAUNCHED.equals(command)) { return lastPearlsLaunched; } else if (CMD_GETSTAT_OPPORTUNITIES.equals(command)) { return lastPearlOpportunities; } return null; } @Override public boolean execute(String command, Integer value) { boolean rc = super.execute(command, value); if (rc) { return true; } if (CLIENTCMD_GETSTAT_RF.equals(command)) { GuiEndergenic.fromServer_lastRfPerTick = value; return true; } else if (CLIENTCMD_GETSTAT_LOST.equals(command)) { GuiEndergenic.fromServer_lastPearlsLost = value; return true; } else if (CLIENTCMD_GETSTAT_LAUNCHED.equals(command)) { GuiEndergenic.fromServer_lastPearlsLaunched = value; return true; } else if (CLIENTCMD_GETSTAT_OPPORTUNITIES.equals(command)) { GuiEndergenic.fromServer_lastPearlOpportunities = value; return true; } return false; } }