package micdoodle8.mods.galacticraft.core.tile;
import java.util.Arrays;
import java.util.List;
import micdoodle8.mods.galacticraft.api.tile.ITileClientUpdates;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* Used as a common interface for any TileEntity with configurable power, pipe, etc sides
*
*/
public interface IMachineSides extends ITileClientUpdates
{
/**
* The different sides which a MachineSide (e.g. electric input)
* can be set to. These are relative to the front of the machine
* (looking at the machine from the normal front).
*/
public enum Face
{
LEFT("l", 2, 1),
RIGHT("r", 0, 2),
REAR("b", 1, 0),
TOP("u", 4, 4),
BOTTOM("d", 3, 3),
NOT_SET("n", 5, 5),
NOT_CONFIGURABLE("fail", 6, 6);
private final String name;
private final int next;
private final int prior;
public static Face[] Horizontals = new Face[] { Face.LEFT, Face.REAR, Face.RIGHT };
public static Face[] AllAvailable = new Face[] { Face.LEFT, Face.REAR, Face.RIGHT, Face.TOP, Face.BOTTOM };
Face(String newname, int theNext, int thePrior)
{
this.name = newname;
this.next = theNext;
this.prior = thePrior;
}
public String getName()
{
return this.name;
}
public Face next()
{
return Face.values()[this.next];
}
public Face prior()
{
return Face.values()[this.prior];
}
}
/*
* All the different possible types of configurable side.
*
* Each of these can correspond to a texture and a function
* according to the block type. Many may be unused.
*
* this.listConfigurableSides() provides a list of which ones
* the tile actually uses.
*/
public enum MachineSide
{
FRONT("front"),
REARDECO("reardeco"),
PLAIN("plain"),
TOP("top"),
BASE("bottom"),
ELECTRIC_IN("elec_in"),
ELECTRIC_OUT("elec_out"),
PIPE_IN("pipe_in"),
PIPE_OUT("pipe_out"),
CUSTOM("custom");
private final String name;
MachineSide(String newname)
{
this.name = newname;
}
}
//--------------METHODS--------------
/**
* The front of this machine, with the graphic.
* This CANNOT be set: use a wrench to change the block's facing to change it
* (If player wants to change the front then rotate the block's facing and all other sides)
*/
public EnumFacing getFront();
/**
* Used internally by tileEntity logic
*/
public default EnumFacing getElectricInputDirection()
{
return null;
}
/**
* Used internally by tileEntity logic
*/
public default EnumFacing getElectricOutputDirection()
{
return null;
}
/**
* Used internally by tileEntity logic
*/
public default EnumFacing getPipeInputDirection()
{
return null;
}
/**
* Used internally by tileEntity logic
*/
public default EnumFacing getPipeOutputDirection()
{
return null;
}
/**
* Used internally by tileEntity logic
*/
public default EnumFacing getCustomDirection()
{
return null;
}
/*
* Returns true if the setting succeeded
* (Also returns true if it is an attempt to set it
* to the same value as it is currently.)
*
* Returns false if the MachineSide is not configurable.
*
* Use isValidForSide() first to test whether the setting
* is valid for the side here.
*/
public default boolean setSide(MachineSide sideToSet, Face newSide)
{
if (this.getConfigurationType() == IMachineSidesProperties.NOT_CONFIGURABLE)
{
return false;
}
for (MachineSidePack msp : this.getAllMachineSides())
{
if (msp.test(sideToSet))
{
msp.set(newSide);
return true;
}
}
return false;
}
/**
* Like setSide() but using index for performance
* - for internal use
*
* index matches the index in getConfigurableSides()
*/
public default boolean setSide(int index, Face newSide)
{
if (this.getConfigurationType() == IMachineSidesProperties.NOT_CONFIGURABLE)
{
return false;
}
MachineSidePack[] msps = this.getAllMachineSides();
if (index >= 0 && index < msps.length)
{
msps[index].set(newSide);
return true;
}
return false;
}
/**
* Like getSide(MachineSide) but using index for performance
* - for internal use
*
* index matches the index in getConfigurableSides()
*/
public default Face getSide(int index)
{
MachineSidePack[] msps = this.getAllMachineSides();
if (index >= 0 && index < msps.length)
return msps[index].get();
return Face.NOT_CONFIGURABLE;
}
public default Face getSide(MachineSide sideToGet)
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.test(sideToGet))
{
return msp.get();
}
}
return Face.NOT_CONFIGURABLE;
}
/*
* Like getSide() but results limited to sides
* which the blockstate is allowed to render
*
* index matches the index in getConfigurableSides()
*/
public default Face getAllowedSide(int index)
{
Face test = this.getSide(index);
for (Face face : this.allowableFaces())
{
if (face == test)
return test;
}
return this.listDefaultFaces()[index];
}
/**
* Returns true if the machine side can be set to this face
*
* By default, returns all allowable configurable faces
*
* Overide this for special tile logic for specific sides
*/
public default boolean isValidForSide(MachineSide sideToSet, Face newSide)
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.test(sideToSet))
{
return Arrays.asList(this.allowableFaces()).contains(newSide);
}
}
//TODO: could maybe return true for default settings for this.getConfigurationType()
//e.g. front == front, top == top, base == bottom
return false;
}
/**
* Can be used for rendering this machine in a GUI
*/
public default MachineSide textureTypeLeft()
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.get() == Face.LEFT)
{
return msp.getRenderType();
}
}
return MachineSide.PLAIN;
}
/**
* Can be used for rendering this machine in a GUI
*/
public default MachineSide textureTypeRight()
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.get() == Face.RIGHT)
{
return msp.getRenderType();
}
}
return MachineSide.PLAIN;
}
/**
* Can be used for rendering this machine in a GUI
*/
public default MachineSide textureTypeRear()
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.get() == Face.REAR)
{
return msp.getRenderType();
}
}
return MachineSide.PLAIN;
}
/**
* Can be used for rendering this machine in a GUI
*/
public default MachineSide textureTypeTop()
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.get() == Face.TOP)
{
return msp.getRenderType();
}
}
return MachineSide.TOP;
}
/**
* Can be used for rendering this machine in a GUI
*/
public default MachineSide textureTypeBase()
{
for(MachineSidePack msp : this.getAllMachineSides())
{
if (msp.get() == Face.BOTTOM)
{
return msp.getRenderType();
}
}
return MachineSide.BASE;
}
/**
* Essential for block rendering - use in getActualState()
* Automatically returns a valid property as long as getConfigurationType() matches the property
* (see BlockMachineTiered for an example)
*
* Override this if you want more options.
*/
public default IMachineSidesProperties.MachineSidesModel buildBlockStateProperty()
{
int length = listConfigurableSides().length;
switch (length)
{
case 1:
return IMachineSidesProperties.getModelForOneFace(this.getAllowedSide(0)).validFor(getConfigurationType());
case 2:
default:
return IMachineSidesProperties.getModelForTwoFaces(this.getAllowedSide(0), this.getAllowedSide(1)).validFor(getConfigurationType());
}
}
/**
* Call this from a Block's getActualState() method
*
* @param state The blockstate prior to addition of this property
* @param tile The tile entity matching the block position (null will return a default value)
* @param renderType The calling block's MACHINESIDES_RENDERTYPE
* @param key The name given to the machine sides property in the calling block e.g. SIDES
*
* @return A blockstate with the property added
*/
public static IBlockState addPropertyForTile(IBlockState state, TileEntity tile, IMachineSidesProperties renderType, PropertyEnum key)
{
if (tile instanceof IMachineSides)
{
IMachineSides tileSides = (IMachineSides) tile;
return state.withProperty(key, tileSides.buildBlockStateProperty());
}
else
return state.withProperty(key, renderType.getDefault());
}
/**
* For testing purposes - Sneak Wrench to activate this
*/
public default void nextSideConfiguration(TileEntity te)
{
if (this.getConfigurationType() == IMachineSidesProperties.NOT_CONFIGURABLE)
{
return;
}
int length = listConfigurableSides().length;
//TODO: adapt result of Face.next() and Face.prior() according to Horiz or All models
switch (length)
{
case 1:
this.setSide(0, this.getSide(0).next());
break;
case 2:
Face leadingSide = this.getSide(0);
if (this.getSide(1) != leadingSide.next())
{
this.setSide(1, leadingSide.prior());
this.setSide(0, leadingSide.next());
}
else
{
this.setSide(1, leadingSide.prior());
}
break;
default:
//Override this for 3 or more configurable sides!
}
if (te.getWorld().isRemote)
{
te.getWorld().markBlockForUpdate(te.getPos());
}
else
{
this.updateAllInDimension();
}
}
/**
* Array with all the configurable sides for this machine
* (not including the front)
*
* Any sides not included in this list are defaults:
* PLAIN for left, right and rear sides
* TOP for top
* BASE for bottom
*/
public MachineSide[] listConfigurableSides();
/**
* Array with the default faces to match entries in listConfigurableSides()
*/
public Face[] listDefaultFaces();
/**
* All the different allowable faces which the configurable sides can be configured to
* (for example it might be horizontal faces only, for this machine)
*/
public default Face[] allowableFaces()
{
return this.getConfigurationType().allowableFaces();
}
/**
* The type of configuration (one or two sides to configure,
* only horizontal settings or all allowed) - this needs to match
* the blockstate model implementation, take it from the Block.
* (Look at TileEnergyStorageModule for an example.)
*/
public IMachineSidesProperties getConfigurationType();
/**
* Call this in getAllMachineSides() or else in the class constructor
*/
public default void initialiseSides()
{
int length = listConfigurableSides().length;
this.setupMachineSides(length);
for (int i = 0; i < length; i++)
{
this.getAllMachineSides()[i] = new MachineSidePack(listConfigurableSides()[i], listDefaultFaces()[i]);
}
}
/**
* The array storing all configurable machine sides
* and their current settings in this tile.
*
* IMPORTANT: Run initialiseSides() in the first call to this
* if it would otherwise be null - see examples in GC code
*/
public MachineSidePack[] getAllMachineSides();
/**
* Create an array of MachineSidePack[length]
* which can later be get using getAllMachineSides()
*/
public void setupMachineSides(int length);
public default void addMachineSidesToNBT(NBTTagCompound par1nbtTagCompound)
{
NBTTagList tagList = new NBTTagList();
for(MachineSidePack msp : this.getAllMachineSides())
{
NBTTagCompound tag = new NBTTagCompound();
msp.writeToNBT(tag);
tagList.appendTag(tag);
}
par1nbtTagCompound.setTag("macsides", tagList);
}
public default void readMachineSidesFromNBT(NBTTagCompound par1nbtTagCompound)
{
NBTTagList tagList = par1nbtTagCompound.getTagList("macsides", 10);
MachineSidePack[] msps = this.getAllMachineSides();
if (tagList.tagCount() == msps.length)
{
for (int i = 0; i < tagList.tagCount(); ++i)
{
msps[i].readFromNBT(tagList.getCompoundTagAt(i));
}
}
}
@Override
public default void buildDataPacket(int[] data)
{
MachineSidePack[] msps = this.getAllMachineSides();
for (int i = 0; i < msps.length; ++i)
{
data[i] = msps[i].get().ordinal();
}
}
@Override
@SideOnly(Side.CLIENT)
public default void updateClient(List<Object> data)
{
MachineSidePack[] msps = this.getAllMachineSides();
for (int i = 0; i < msps.length; ++i)
{
msps[i].set((Integer) data.get(i + 1));
}
((TileEntity)this).getWorld().markBlockForUpdate(((TileEntity)this).getPos());
}
/**
* A container object for Machine Sides settings
* This should be implemented in an array by each TileEntity
*
*/
public class MachineSidePack
{
private MachineSide theSide;
private Face currentFace;
public MachineSidePack(MachineSide type, Face defaultFace)
{
assert(type != null);
this.theSide = type;
this.set(defaultFace);
}
public void writeToNBT(NBTTagCompound tag)
{
tag.setInteger("ts", this.theSide.ordinal());
tag.setInteger("cf", this.currentFace.ordinal());
}
public void readFromNBT(NBTTagCompound tag)
{
int ts = -1;
int cf = -1;
if (tag.hasKey("ts"))
{
ts = tag.getInteger("ts");
}
if (tag.hasKey("cf"))
{
cf = tag.getInteger("cf");
}
if (ts == theSide.ordinal() && cf >= 0 && cf < Face.values().length)
{
this.currentFace = Face.values()[cf];
}
}
public void set(Face face)
{
this.currentFace = (face == null) ? Face.NOT_SET : face;
}
public void set(int index)
{
if (index >= 0 && index < Face.values().length)
this.currentFace = Face.values()[index];
}
public Face get()
{
return this.currentFace;
}
public MachineSide getRenderType()
{
return this.theSide;
}
public boolean test(MachineSide type)
{
return this.theSide == type;
}
}
}