549 lines
17 KiB
Java
Executable file
549 lines
17 KiB
Java
Executable file
/*******************************************************************************
|
|
* Copyright (c) 2012-2013 Yancarlo Ramsey and CJ Bowman
|
|
* Licensed as open source with restrictions. Please see attached LICENSE.txt.
|
|
******************************************************************************/
|
|
package com.kaijin.AdvPowerMan.tileentities;
|
|
|
|
import com.kaijin.AdvPowerMan.AdvancedPowerManagement;
|
|
import com.kaijin.AdvPowerMan.Info;
|
|
import com.kaijin.AdvPowerMan.MovingAverage;
|
|
import com.kaijin.AdvPowerMan.Utils;
|
|
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
import ic2.api.energy.EnergyNet;
|
|
import ic2.api.energy.event.EnergyTileLoadEvent;
|
|
import ic2.api.energy.tile.IEnergySource;
|
|
import ic2.api.item.ElectricItem;
|
|
import ic2.api.item.IElectricItem;
|
|
import io.netty.buffer.ByteBuf;
|
|
import net.minecraft.inventory.IInventory;
|
|
import net.minecraft.inventory.ISidedInventory;
|
|
import net.minecraft.item.Item;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.nbt.NBTTagList;
|
|
import net.minecraft.network.Packet;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraftforge.common.MinecraftForge;
|
|
import net.minecraftforge.common.util.Constants;
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
|
|
|
import java.io.IOException;
|
|
|
|
public class TEBatteryStation extends TECommonBench implements IEnergySource, IInventory, ISidedInventory{
|
|
public int opMode;
|
|
|
|
// Base values
|
|
public int packetSize;
|
|
public int currentEnergy = 0;
|
|
|
|
private boolean invChanged = false;
|
|
private boolean hasEnoughItems = false;
|
|
|
|
// For outside texture display
|
|
public boolean doingWork;
|
|
|
|
private int energyOut = 0;
|
|
public MovingAverage outputTracker = new MovingAverage(12);
|
|
|
|
private static final int[] BatteryStationSideInput = {Info.BS_SLOT_INPUT};
|
|
private static final int[] BatteryStationSideOutput = {Info.BS_SLOT_OUTPUT};
|
|
private static final int[] BatteryStationSideInOut = {Info.BS_SLOT_INPUT, Info.BS_SLOT_OUTPUT};
|
|
|
|
public TEBatteryStation(){ // Default constructor used only when loading tile entity from world save
|
|
super();
|
|
// Do nothing else; Creating the inventory array and loading previous
|
|
// values will be handled in NBT read method momentarily.
|
|
}
|
|
|
|
public TEBatteryStation(int i){ // Constructor used when placing a new tile entity, to set up correct parameters
|
|
super();
|
|
contents = new ItemStack[14];
|
|
|
|
// base tier = what we're passed, so 1, 2 or 3
|
|
baseTier = i;
|
|
opMode = 1;
|
|
initializeValues();
|
|
}
|
|
|
|
private void initializeValues(){
|
|
powerTier = baseTier;
|
|
// Output math = 32 for tier 1, 128 for tier 2, 512 for tier 3
|
|
packetSize = (int) Math.pow(2.0D, EnergyNet.instance.getPowerFromTier(powerTier));
|
|
|
|
}
|
|
|
|
// IC2 API functions
|
|
|
|
@Override
|
|
public boolean emitsEnergyTo(TileEntity receiver, ForgeDirection direction){
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public double getOfferedEnergy(){
|
|
return (!receivingRedstoneSignal()) ? Math.min(currentEnergy, packetSize) : 0;
|
|
}
|
|
|
|
@Override
|
|
public void drawEnergy(double amount){
|
|
if(!receivingRedstoneSignal()){
|
|
drainPowerSource();
|
|
outputTracker.tick((int) amount);
|
|
currentEnergy -= amount;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getSourceTier(){
|
|
return powerTier; // XXX: cause I dunno what to put... / READ THE JAVADOC YOU NOOB - xbony2
|
|
}
|
|
|
|
// End IC2 API
|
|
|
|
@Override
|
|
public int getGuiID(){
|
|
return Info.GUI_ID_BATTERY_STATION;
|
|
}
|
|
|
|
/**
|
|
* This will cause the block to drop anything inside it, create a new item
|
|
* in the world of its type, invalidate the tile entity, remove itself from
|
|
* the IC2 EnergyNet and clear the block space (set it to air)
|
|
*/
|
|
@Override
|
|
protected void selfDestroy(){
|
|
dropContents();
|
|
ItemStack stack = new ItemStack(AdvancedPowerManagement.blockAdvPwrMan, 1, Info.BS_META + baseTier - 1);
|
|
dropItem(stack);
|
|
worldObj.setBlockToAir(xCoord, yCoord, zCoord);
|
|
this.invalidate();
|
|
}
|
|
|
|
public boolean isItemValid(int slot, ItemStack stack){
|
|
// Decide if the item is a valid IC2 electrical item
|
|
if(stack != null && stack.getItem() instanceof IElectricItem){
|
|
IElectricItem item = (IElectricItem) (stack.getItem());
|
|
// Is the item appropriate for this slot?
|
|
if(slot == Info.BS_SLOT_OUTPUT)
|
|
return true; // GUI won't allow placement of items here, but if
|
|
// the bench or an external machine does, it
|
|
// should at least let it sit there as long as
|
|
// it's an electrical item.
|
|
if(item.canProvideEnergy(stack) && item.getTier(stack) <= powerTier){
|
|
if((slot >= Info.BS_SLOT_POWER_START && slot < Info.BS_SLOT_POWER_START + 12) || slot == Info.BS_SLOT_INPUT)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Reads a tile entity from NBT.
|
|
*/
|
|
@Override
|
|
public void readFromNBT(NBTTagCompound nbttagcompound){
|
|
super.readFromNBT(nbttagcompound);
|
|
|
|
if(Info.isDebugging)
|
|
System.out.println("BS ID: " + nbttagcompound.getString("id"));
|
|
|
|
baseTier = nbttagcompound.getInteger("baseTier");
|
|
opMode = nbttagcompound.getInteger("opMode");
|
|
currentEnergy = nbttagcompound.getInteger("currentEnergy");
|
|
|
|
// Our inventory
|
|
contents = new ItemStack[Info.BS_INVENTORY_SIZE];
|
|
NBTTagList nbttaglist = nbttagcompound.getTagList("Items", Constants.NBT.TAG_COMPOUND);
|
|
for(int i = 0; i < nbttaglist.tagCount(); ++i){
|
|
NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbttaglist.getCompoundTagAt(i);
|
|
int j = nbttagcompound1.getByte("Slot") & 255;
|
|
|
|
if(j >= 0 && j < contents.length){
|
|
contents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
|
|
}
|
|
}
|
|
|
|
// We can calculate these, no need to save/load them.
|
|
initializeValues();
|
|
}
|
|
|
|
/**
|
|
* Writes a tile entity to NBT.
|
|
*/
|
|
@Override
|
|
public void writeToNBT(NBTTagCompound nbttagcompound){
|
|
super.writeToNBT(nbttagcompound);
|
|
|
|
nbttagcompound.setInteger("baseTier", baseTier);
|
|
nbttagcompound.setInteger("opMode", opMode);
|
|
nbttagcompound.setInteger("currentEnergy", currentEnergy);
|
|
|
|
// Our inventory
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
for(int i = 0; i < contents.length; ++i){
|
|
if(contents[i] != null){
|
|
// if (ChargingBench.isDebugging)
|
|
// System.out.println("WriteNBT contents[" + i + "] stack tag: "
|
|
// + contents[i].stackTagCompound);
|
|
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
|
nbttagcompound1.setByte("Slot", (byte) i);
|
|
contents[i].writeToNBT(nbttagcompound1);
|
|
nbttaglist.appendTag(nbttagcompound1);
|
|
}
|
|
}
|
|
nbttagcompound.setTag("Items", nbttaglist);
|
|
}
|
|
|
|
@Override
|
|
public void updateEntity(){
|
|
if(AdvancedPowerManagement.proxy.isClient())
|
|
return;
|
|
|
|
if(!initialized && worldObj != null){
|
|
EnergyTileLoadEvent loadEvent = new EnergyTileLoadEvent(this);
|
|
MinecraftForge.EVENT_BUS.post(loadEvent);
|
|
// EnergyNet.getForWorld(worldObj).addTileEntity(this);
|
|
initialized = true;
|
|
}
|
|
|
|
boolean lastWorkState = doingWork;
|
|
doingWork = false;
|
|
invChanged = false;
|
|
hasEnoughItems = true;
|
|
|
|
if(!receivingRedstoneSignal()){
|
|
// Work done only when not redstone powered
|
|
drainPowerSource();
|
|
}
|
|
|
|
// Work done every tick
|
|
moveOutputItems();
|
|
repositionItems();
|
|
acceptInputItems();
|
|
|
|
if(invChanged){
|
|
this.markDirty(); // This doesn't need to be called multiple times, so it gets flagged to happen here if needed.
|
|
}
|
|
|
|
// Trigger this only when it would need to update the client texture
|
|
if(lastWorkState != doingWork){
|
|
// if (ChargingBench.isDebugging)
|
|
// System.out.println("TE oldChargeLevel: " + oldChargeLevel +
|
|
// " chargeLevel: " + chargeLevel);
|
|
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is constantly called, wether something is in it or not.
|
|
* It checks that stuff automatically, assuming it isn't powered by redstone.
|
|
*/
|
|
private void drainPowerSource(){
|
|
hasEnoughItems = false;
|
|
for(int i = Info.BS_SLOT_POWER_START; i < Info.BS_SLOT_POWER_START + 12; i++){
|
|
if(currentEnergy >= packetSize){
|
|
hasEnoughItems = true;
|
|
break;
|
|
}
|
|
|
|
ItemStack stack = contents[i];
|
|
if(stack != null && stack.getItem() instanceof IElectricItem && stack.stackSize == 1){
|
|
IElectricItem item = (IElectricItem) stack.getItem();
|
|
if(item.getTier(stack) <= powerTier && item.canProvideEnergy(stack)){
|
|
//System.out.println("Do we even get here?"); we do -xbony2
|
|
Item emptyItem = item.getEmptyItem(stack);
|
|
//Funfact: nobody use ids anymore.
|
|
if(true/*stack.getItem() == item.getChargedItem(stack)*/){ // this doesn't work, but who cares?
|
|
double transferLimit = item.getTransferLimit(stack);
|
|
// int amountNeeded = baseMaxOutput - currentEnergy;
|
|
if(transferLimit == 0)
|
|
transferLimit = packetSize;
|
|
// if (transferLimit > amountNeeded) transferLimit =
|
|
// amountNeeded;
|
|
|
|
double chargeReturned = ElectricItem.manager.discharge(stack, transferLimit, powerTier, false, false, false);
|
|
//^hold up... if it discharges it, why would it return anything but zero?
|
|
System.out.println(chargeReturned);
|
|
if(chargeReturned > 0){
|
|
System.out.println("THIS *SHOULD* BE WORKING");
|
|
// Add the energy we received to our current energy
|
|
// level
|
|
currentEnergy += chargeReturned;
|
|
doingWork = true;
|
|
}else{
|
|
System.out.println("BROKEN SHIT GOIN' ON RIGHT HERE RIGHT NOW");
|
|
}
|
|
|
|
// Workaround for buggy IC2 API .discharge that
|
|
// automatically switches stack to emptyItemID but
|
|
// leaves a stackTagCompound on it, so it can't be
|
|
// stacked with never-used empties
|
|
if(item.getChargedItem(stack) != emptyItem
|
|
&& (chargeReturned < transferLimit || ElectricItem.manager.discharge(stack, 1, powerTier, false, true, false) == 0)){
|
|
// if (ChargingBench.isDebugging)
|
|
// System.out.println("Switching to emptyItemID: " +
|
|
// emptyItemID + " from stack.itemID: " +
|
|
// stack.itemID + " - chargedItemID: " +
|
|
// chargedItemID);
|
|
setInventorySlotContents(i, new ItemStack(emptyItem, 1, 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* First, check the output slot to see if it's empty. If so, look to see if
|
|
* there are any fully DIScharged items in the main inventory. Move the
|
|
* first empty item to the output slot. If output slot contains stackable
|
|
* empties, check for matching empties to add to that stack.
|
|
*/
|
|
private void moveOutputItems(){
|
|
rejectInvalidInput();
|
|
|
|
ItemStack outputStack = contents[Info.BS_SLOT_OUTPUT];
|
|
if(outputStack == null || (outputStack.isStackable() && outputStack.stackSize < outputStack.getMaxStackSize())){
|
|
// Output slot could receive item(s). Try to find something to move
|
|
// there.
|
|
for(int slot = 0; slot < contents.length; ++slot){
|
|
if(slot == Info.BS_SLOT_OUTPUT)
|
|
continue;
|
|
|
|
ItemStack currentStack = contents[slot];
|
|
if(currentStack != null && currentStack.getItem() instanceof IElectricItem){
|
|
IElectricItem powerSource = (IElectricItem) (currentStack.getItem());
|
|
if(powerSource.getTier(currentStack) <= powerTier) // &&
|
|
// powerSource.canProvideEnergy()
|
|
{
|
|
int emptyItemID = Item.getIdFromItem(powerSource.getEmptyItem(currentStack));
|
|
int chargedItemID = Item.getIdFromItem(powerSource.getChargedItem(currentStack));
|
|
if(emptyItemID != chargedItemID){
|
|
if(Item.getIdFromItem(currentStack.getItem()) == emptyItemID){
|
|
// Pick Me
|
|
if(outputStack == null){
|
|
contents[Info.BS_SLOT_OUTPUT] = currentStack;
|
|
contents[slot] = null;
|
|
}else{
|
|
// We already know the stack isn't full yet
|
|
contents[Info.BS_SLOT_OUTPUT].stackSize++;
|
|
contents[slot].stackSize--;
|
|
if(contents[slot].stackSize < 1)
|
|
contents[slot] = null;
|
|
}
|
|
invChanged = true;
|
|
break;
|
|
}
|
|
}else if(outputStack == null){
|
|
boolean empty = ElectricItem.manager.discharge(currentStack, 1, powerTier, true, true, false) == 0;
|
|
if(empty){
|
|
// Pick Me
|
|
contents[Info.BS_SLOT_OUTPUT] = currentStack;
|
|
contents[slot] = null;
|
|
invChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adjust positions of items in inventory to preserve FIFO order where
|
|
* possible.
|
|
*/
|
|
private void repositionItems(){
|
|
final int lastIndex = Info.BS_SLOT_POWER_START + 11;
|
|
int vacancy = Info.BS_SLOT_POWER_START;
|
|
while(vacancy < lastIndex && contents[vacancy] != null){
|
|
vacancy++;
|
|
}
|
|
int hunt = vacancy + 1;
|
|
while(vacancy < lastIndex && hunt <= lastIndex) // Mix of < and <= is
|
|
// not an error: Avoids
|
|
// needing +1 or -1
|
|
// added to something.
|
|
{
|
|
if(contents[vacancy] == null && contents[hunt] != null){
|
|
contents[vacancy] = contents[hunt];
|
|
contents[hunt] = null;
|
|
invChanged = true;
|
|
vacancy++;
|
|
}
|
|
hunt++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if there are any items in the input slot. If so, check to
|
|
* see if there are any free discharging slots. If so, move one from the
|
|
* input slot to a free discharging slot.
|
|
*/
|
|
private void acceptInputItems(){
|
|
// System.out.println("aII: opMode " + opMode);
|
|
ItemStack stack = contents[Info.BS_SLOT_INPUT];
|
|
if(stack == null || !(stack.getItem() instanceof IElectricItem) || (opMode == 1 && hasEnoughItems))
|
|
return;
|
|
|
|
IElectricItem item = (IElectricItem) stack.getItem();
|
|
if(item.canProvideEnergy(stack)){
|
|
// Input slot contains a power source. If possible, move one of it
|
|
// into the discharging area.
|
|
for(int slot = Info.BS_SLOT_POWER_START; slot < Info.BS_SLOT_POWER_START + 12; ++slot){
|
|
if(contents[slot] == null){
|
|
// Grab one unit from input and move it to the selected
|
|
// slot.
|
|
contents[slot] = decrStackSize(Info.BS_SLOT_INPUT, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void rejectInvalidInput(){
|
|
// Move item from input to output if not valid. (Wrong tier or not
|
|
// electric item.)
|
|
if(contents[Info.BS_SLOT_INPUT] != null && contents[Info.BS_SLOT_OUTPUT] == null){
|
|
if(!isItemValid(Info.BS_SLOT_INPUT, contents[Info.BS_SLOT_INPUT])){
|
|
contents[Info.BS_SLOT_OUTPUT] = contents[Info.BS_SLOT_INPUT];
|
|
contents[Info.BS_SLOT_INPUT] = null;
|
|
invChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add up amount of energy stored in items in all slots except output and
|
|
// return that value
|
|
public int getTotalEnergy(){
|
|
int energySum = 0;
|
|
for(int i = 0; i < Info.BS_SLOT_POWER_START + 12; i++){
|
|
if(i == Info.BS_SLOT_OUTPUT)
|
|
continue;
|
|
|
|
final ItemStack stack = contents[i];
|
|
if(stack != null && stack.getItem() instanceof IElectricItem && stack.stackSize == 1){
|
|
final IElectricItem item = (IElectricItem) (stack.getItem());
|
|
if(item.getTier(stack) <= powerTier && item.canProvideEnergy(stack)
|
|
&& Item.getIdFromItem(stack.getItem()) == Item.getIdFromItem(item.getChargedItem(stack))){
|
|
final double chargeReturned = ElectricItem.manager.discharge(stack, Integer.MAX_VALUE, powerTier, true, true, false);
|
|
if(chargeReturned > 0){
|
|
// Add the energy we received to our current energy
|
|
// level
|
|
energySum += chargeReturned;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return energySum;
|
|
}
|
|
|
|
// Networking stuff
|
|
|
|
@Override
|
|
public Packet getDescriptionPacket(){
|
|
return createDescPacket();
|
|
}
|
|
|
|
@Override
|
|
protected void addUniqueDescriptionData(ByteBuf data) throws IOException{
|
|
data.writeBoolean(doingWork);
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
@Override
|
|
public void receiveDescriptionData(int packetID, ByteBuf stream){
|
|
boolean b = doingWork;
|
|
// try
|
|
// {
|
|
b = stream.readBoolean();
|
|
/*
|
|
* } catch (IOException e) { logDescPacketError(e); return; }
|
|
*/
|
|
doingWork = b;
|
|
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
|
|
}
|
|
|
|
@Override
|
|
public void receiveGuiButton(int buttonID){
|
|
if(buttonID == 0){
|
|
opMode ^= 1;
|
|
}
|
|
}
|
|
|
|
// ISidedInventory
|
|
|
|
/*
|
|
* @Override public int getStartInventorySide(ForgeDirection side) { switch
|
|
* (side) { case UP: case DOWN: return Info.BS_SLOT_INPUT; default: return
|
|
* Info.BS_SLOT_OUTPUT; } }
|
|
*
|
|
* @Override public int getSizeInventorySide(ForgeDirection side) { // Each
|
|
* side accesses a single slot return 1; }
|
|
*/
|
|
|
|
@Override
|
|
public int[] getAccessibleSlotsFromSide(int side){
|
|
return BatteryStationSideInOut; // Testing I/O constraint methods
|
|
// func_102007_a, func_102008_b
|
|
}
|
|
|
|
@Override
|
|
public boolean isItemValidForSlot(int i, ItemStack stack){
|
|
if(i == Info.BS_SLOT_INPUT)
|
|
return Utils.isItemDrainable(stack, powerTier);
|
|
return false;
|
|
}
|
|
|
|
// Returns true if automation can insert the given item in the given slot
|
|
// from the given side. Args: Slot, item, side
|
|
@Override
|
|
public boolean canInsertItem(int i, ItemStack itemstack, int j) // canInsertItem
|
|
{
|
|
if(i == Info.BS_SLOT_INPUT)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Returns true if automation can extract the given item in the given slot
|
|
// from the given side. Args: Slot, item, side
|
|
@Override
|
|
public boolean canExtractItem(int i, ItemStack itemstack, int j){ // canExtractItem
|
|
if(i == Info.BS_SLOT_OUTPUT)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// IInventory
|
|
|
|
@Override
|
|
public int getSizeInventory(){
|
|
// Only input/output slots are accessible to machines
|
|
return 2;
|
|
}
|
|
|
|
@Override
|
|
public String getInventoryName(){
|
|
switch(baseTier){
|
|
case 1:
|
|
return Info.KEY_BLOCK_NAMES[8] + Info.KEY_NAME_SUFFIX;
|
|
case 3:
|
|
return Info.KEY_BLOCK_NAMES[9] + Info.KEY_NAME_SUFFIX;
|
|
case 4:
|
|
return Info.KEY_BLOCK_NAMES[10] + Info.KEY_NAME_SUFFIX;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
@Override
|
|
public void markDirty(int slot){
|
|
if(slot == Info.BS_SLOT_INPUT || slot == Info.BS_SLOT_OUTPUT){
|
|
rejectInvalidInput();
|
|
}
|
|
super.markDirty();
|
|
}
|
|
}
|