/* * Copyright (c) CovertJaguar, 2014 http://railcraft.info * * This code is the property of CovertJaguar * and may only be used with explicit written * permission unless otherwise specified on the * license page at http://railcraft.info/wiki/info:license. */ package mods.railcraft.common.blocks.machine.epsilon; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import mods.railcraft.api.electricity.IElectricGrid; import mods.railcraft.api.tracks.ITrackInstance; import mods.railcraft.api.tracks.ITrackLockdown; import mods.railcraft.common.blocks.RailcraftBlocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.IIcon; import mods.railcraft.common.blocks.machine.IEnumMachine; import mods.railcraft.common.blocks.machine.TileMachineBase; import mods.railcraft.common.blocks.tracks.*; import mods.railcraft.common.plugins.forge.PowerPlugin; import mods.railcraft.common.plugins.forge.WorldPlugin; import mods.railcraft.common.util.effects.EffectManager; import mods.railcraft.common.util.misc.Game; import mods.railcraft.common.util.misc.MiscTools; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; /** * * @author CovertJaguar <http://www.railcraft.info> */ public class TileForceTrackEmitter extends TileMachineBase implements IElectricGrid { private static final double BASE_DRAW = 22; private static final double CHARGE_PER_TRACK = 2; private static final int TICKS_PER_ACTION = 4; private static final int TICKS_PER_REFRESH = 64; public static final int MAX_TRACKS = 64; private final ChargeHandler chargeHandler = new ChargeHandler(this, ChargeHandler.ConnectType.BLOCK, 0.0); private boolean powered; private ForgeDirection facing = ForgeDirection.NORTH; private int numTracks; private State state = State.RETRACTED; private static enum State { EXTENDED, RETRACTED, EXTENDING, RETRACTING, HALTED; private void doAction(TileForceTrackEmitter emitter) { switch (this) { case EXTENDING: emitter.extend(); break; case RETRACTING: emitter.retract(); break; case EXTENDED: emitter.extended(); break; } } } @Override public void onNeighborBlockChange(Block block) { super.onNeighborBlockChange(block); checkRedstone(); } @Override public void onBlockPlacedBy(EntityLivingBase entityliving, ItemStack stack) { super.onBlockPlacedBy(entityliving, stack); facing = MiscTools.getHorizontalSideClosestToPlayer(worldObj, xCoord, yCoord, zCoord, entityliving); checkRedstone(); } private void checkRedstone() { if (Game.isNotHost(getWorld())) return; boolean p = PowerPlugin.isBlockBeingPowered(worldObj, xCoord, yCoord, zCoord); if (powered != p) { powered = p; sendUpdateToClient(); } } @Override public void onBlockRemoval() { super.onBlockRemoval(); while (numTracks > 0) { int x = xCoord + numTracks * facing.offsetX; int y = yCoord + 1; int z = zCoord + numTracks * facing.offsetZ; removeTrack(x, y, z); } } @Override public void updateEntity() { super.updateEntity(); if (Game.isNotHost(getWorld())) return; double draw = getDraw(numTracks); if (powered && chargeHandler.removeCharge(draw) >= draw) switch (state) { case RETRACTED: case RETRACTING: case HALTED: state = State.EXTENDING; break; case EXTENDED: if (clock % TICKS_PER_REFRESH == 0) state = State.EXTENDING; break; } else if (state == State.EXTENDED || state == State.EXTENDING || state == State.HALTED) state = State.RETRACTING; state.doAction(this); chargeHandler.tick(); } private void spawnParticles(int x, int y, int z) { EffectManager.instance.forceTrackSpawnEffect(worldObj, x, y, z); } private void extended() { TileEntity tile = tileCache.getTileOnSide(ForgeDirection.UP); if (tile instanceof TileTrack) { TileTrack trackTile = (TileTrack) tile; ITrackInstance track = trackTile.getTrackInstance(); if (track instanceof ITrackLockdown) ((ITrackLockdown) track).releaseCart(); } } private void extend() { if (!hasPowerToExtend()) state = State.HALTED; if (numTracks >= MAX_TRACKS) state = State.EXTENDED; else if (clock % TICKS_PER_ACTION == 0) { int x = xCoord + (numTracks + 1) * facing.offsetX; int y = yCoord + 1; int z = zCoord + (numTracks + 1) * facing.offsetZ; if (WorldPlugin.blockExists(worldObj, x, y, z)) { Block block = WorldPlugin.getBlock(worldObj, x, y, z); EnumTrackMeta meta; if (facing == ForgeDirection.NORTH || facing == ForgeDirection.SOUTH) meta = EnumTrackMeta.NORTH_SOUTH; else meta = EnumTrackMeta.EAST_WEST; if (!placeTrack(x, y, z, block, meta) && !claimTrack(x, y, z, block, meta)) state = State.EXTENDED; } else state = State.HALTED; } } private boolean placeTrack(int x, int y, int z, Block block, EnumTrackMeta meta) { if (WorldPlugin.blockIsAir(worldObj, x, y, z, block)) { spawnParticles(x, y, z); TileTrack track = TrackTools.placeTrack(EnumTrack.FORCE.getTrackSpec(), worldObj, x, y, z, meta.ordinal()); ((TrackForce) track.getTrackInstance()).setEmitter(this); numTracks++; return true; } return false; } private boolean claimTrack(int x, int y, int z, Block block, EnumTrackMeta meta) { if (block != RailcraftBlocks.getBlockTrack()) return false; if (TrackTools.getTrackMetaEnum(worldObj, block, null, x, y, z) != meta) return false; TileEntity tile = WorldPlugin.getBlockTile(worldObj, x, y, z); if (!TrackTools.isTrackSpec(tile, EnumTrack.FORCE.getTrackSpec())) return false; TrackForce track = (TrackForce) ((TileTrack) tile).getTrackInstance(); TileForceTrackEmitter emitter = track.getEmitter(); if (emitter == null || emitter == this) { track.setEmitter(this); numTracks++; return true; } return false; } public int getNumberOfTracks() { return numTracks; } public static double getDraw(int tracks) { return BASE_DRAW + CHARGE_PER_TRACK * tracks; } public boolean hasPowerToExtend() { return chargeHandler.getCharge() >= getDraw(numTracks + 1); } private void retract() { if (numTracks <= 0) state = State.RETRACTED; else if (clock % TICKS_PER_ACTION == 0) { int x = xCoord + numTracks * facing.offsetX; int y = yCoord + 1; int z = zCoord + numTracks * facing.offsetZ; removeTrack(x, y, z); } } private void removeTrack(int x, int y, int z) { if (WorldPlugin.blockExists(worldObj, x, y, z) && TrackTools.isTrackAt(worldObj, x, y, z, EnumTrack.FORCE)) { spawnParticles(x, y, z); WorldPlugin.setBlockToAir(worldObj, x, y, z); } numTracks--; } @Override public ChargeHandler getChargeHandler() { return chargeHandler; } @Override public TileEntity getTile() { return this; } @Override public IEnumMachine getMachineType() { return EnumMachineEpsilon.FORCE_TRACK_EMITTER; } @Override public IIcon getIcon(int side) { if (side == facing.ordinal()) return getMachineType().getTexture(powered ? 7 : 8); return getMachineType().getTexture(powered ? 0 : 6); } @Override public boolean rotateBlock(ForgeDirection axis) { if (Game.isNotHost(worldObj)) return false; if (state != State.RETRACTED) return false; if (axis == ForgeDirection.UP || axis == ForgeDirection.DOWN) return false; if (facing == axis) facing = axis.getOpposite(); else facing = axis; numTracks = 0; markBlockForUpdate(); notifyBlocksOfNeighborChange(); return true; } @Override public void writeToNBT(NBTTagCompound data) { super.writeToNBT(data); chargeHandler.writeToNBT(data); data.setBoolean("powered", powered); data.setByte("facing", (byte) facing.ordinal()); data.setInteger("numTracks", numTracks); data.setString("state", state.name()); } @Override public void readFromNBT(NBTTagCompound data) { super.readFromNBT(data); chargeHandler.readFromNBT(data); powered = data.getBoolean("powered"); facing = ForgeDirection.getOrientation(data.getByte("facing")); numTracks = data.getInteger("numTracks"); state = State.valueOf(data.getString("state")); } @Override public void writePacketData(DataOutputStream data) throws IOException { super.writePacketData(data); data.writeBoolean(powered); data.writeByte((byte) facing.ordinal()); } @Override public void readPacketData(DataInputStream data) throws IOException { super.readPacketData(data); boolean update = false; boolean p = data.readBoolean(); if (powered != p) { powered = p; update = true; } byte f = data.readByte(); if (facing != ForgeDirection.getOrientation(f)) { facing = ForgeDirection.getOrientation(f); update = true; } if (update) markBlockForUpdate(); } public ForgeDirection getFacing() { return facing; } }