package mcjty.rftools.blocks.shield;
import cpw.mods.fml.common.Optional;
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.Callback;
import li.cil.oc.api.machine.Context;
import li.cil.oc.api.network.SimpleComponent;
import mcjty.lib.entity.GenericEnergyReceiverTileEntity;
import mcjty.lib.network.Argument;
import mcjty.lib.varia.BlockTools;
import mcjty.lib.varia.Coordinate;
import mcjty.lib.varia.Logging;
import mcjty.rftools.blocks.RedstoneMode;
import mcjty.rftools.blocks.shield.filters.*;
import mcjty.rftools.items.shapecard.ShapeCardItem;
import mcjty.rftools.items.smartwrench.SmartWrenchSelector;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.common.util.ForgeDirection;
import java.util.*;
@Optional.InterfaceList({
@Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers"),
@Optional.Interface(iface = "dan200.computercraft.api.peripheral.IPeripheral", modid = "ComputerCraft")})
public class ShieldTEBase extends GenericEnergyReceiverTileEntity implements IInventory, SmartWrenchSelector, SimpleComponent, IPeripheral {
public static final String CMD_SHIELDVISMODE = "shieldVisMode";
public static final String CMD_APPLYCAMO = "applyCamo";
public static final String CMD_DAMAGEMODE = "damageMode";
public static final String CMD_RSMODE = "rsMode";
public static final String CMD_ADDFILTER = "addFilter";
public static final String CMD_DELFILTER = "delFilter";
public static final String CMD_UPFILTER = "upFilter";
public static final String CMD_DOWNFILTER = "downFilter";
public static final String CMD_GETFILTERS = "getFilters";
public static final String CMD_SETCOLOR = "setColor";
public static final String CLIENTCMD_GETFILTERS = "getFilters";
public static final String COMPONENT_NAME = "shield_projector";
private RedstoneMode redstoneMode = RedstoneMode.REDSTONE_IGNORED;
private DamageTypeMode damageMode = DamageTypeMode.DAMAGETYPE_GENERIC;
// If true the shield is currently made.
private boolean shieldComposed = false;
// The meta value for the template blocks that were used.
private int templateMeta = 0;
// If true the shield is currently active.
private boolean shieldActive = false;
// Timeout in case power is low. Here we wait a bit before trying again.
private int powerTimeout = 0;
private int shieldColor;
// Render pass for the camo block.
private int camoRenderPass = 0;
private int supportedBlocks;
private float damageFactor = 1.0f;
private float costFactor = 1.0f;
// Filter list.
private final List<ShieldFilter> filters = new ArrayList<ShieldFilter>();
private ShieldRenderingMode shieldRenderingMode = ShieldRenderingMode.MODE_SHIELD;
private List<RelCoordinate> shieldBlocks = new ArrayList<RelCoordinate>();
private ItemStack stacks[] = new ItemStack[ShieldContainer.BUFFER_SIZE];
public ShieldTEBase(int maxEnergy, int maxReceive) {
super(maxEnergy, maxReceive);
}
public void setSupportedBlocks(int supportedBlocks) {
this.supportedBlocks = supportedBlocks;
}
public void setDamageFactor(float factor) {
this.damageFactor = factor;
}
public void setCostFactor(float factor) {
this.costFactor = factor;
}
@Override
@Optional.Method(modid = "ComputerCraft")
public String getType() {
return COMPONENT_NAME;
}
@Override
@Optional.Method(modid = "ComputerCraft")
public String[] getMethodNames() {
return new String[] { "getDamageMode", "setDamageMode", "getRedstoneMode", "setRedstoneMode", "getShieldRenderingMode", "setShieldRenderingMode", "isShieldActive", "isShieldComposed",
"composeShield", "composeShieldDsc", "decomposeShield" };
}
@Override
@Optional.Method(modid = "ComputerCraft")
public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException {
switch (method) {
case 0: return new Object[] { getDamageMode().getDescription() };
case 1: return setDamageMode((String) arguments[0]);
case 2: return new Object[] { getRedstoneMode().getDescription() };
case 3: return setRedstoneMode((String) arguments[0]);
case 4: return new Object[] { getShieldRenderingMode().getDescription() };
case 5: return setShieldRenderingMode((String) arguments[0]);
case 6: return new Object[] { isShieldActive() };
case 7: return new Object[] { isShieldComposed() };
case 8: return composeShieldComp(false);
case 9: return composeShieldComp(true);
case 10: return decomposeShieldComp();
}
return new Object[0];
}
@Override
@Optional.Method(modid = "ComputerCraft")
public void attach(IComputerAccess computer) {
}
@Override
@Optional.Method(modid = "ComputerCraft")
public void detach(IComputerAccess computer) {
}
@Override
@Optional.Method(modid = "ComputerCraft")
public boolean equals(IPeripheral other) {
return false;
}
@Override
@Optional.Method(modid = "OpenComputers")
public String getComponentName() {
return COMPONENT_NAME;
}
@Callback(doc = "Get the current damage mode for the shield. 'Generic' means normal damage while 'Player' means damage like a player would do", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] getDamageMode(Context context, Arguments args) throws Exception {
return new Object[] { getDamageMode().getDescription() };
}
@Callback(doc = "Set the current damage mode for the shield. 'Generic' means normal damage while 'Player' means damage like a player would do", setter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] setDamageMode(Context context, Arguments args) throws Exception {
String mode = args.checkString(0);
return setDamageMode(mode);
}
private Object[] setDamageMode(String mode) {
DamageTypeMode damageMode = DamageTypeMode.getMode(mode);
if (damageMode == null) {
throw new IllegalArgumentException("Not a valid mode");
}
setDamageMode(damageMode);
return null;
}
@Callback(doc = "Get the current redstone mode. Values are 'Ignored', 'Off', or 'On'", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] getRedstoneMode(Context context, Arguments args) throws Exception {
return new Object[] { getRedstoneMode().getDescription() };
}
@Callback(doc = "Set the current redstone mode. Values are 'Ignored', 'Off', or 'On'", setter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] setRedstoneMode(Context context, Arguments args) throws Exception {
String mode = args.checkString(0);
return setRedstoneMode(mode);
}
private Object[] setRedstoneMode(String mode) {
RedstoneMode redstoneMode = RedstoneMode.getMode(mode);
if (redstoneMode == null) {
throw new IllegalArgumentException("Not a valid mode");
}
setRedstoneMode(redstoneMode);
return null;
}
@Callback(doc = "Get the current shield rendering mode. Values are 'Invisible', 'Shield', or 'Solid'", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] getShieldRenderingMode(Context context, Arguments args) throws Exception {
return new Object[] { getShieldRenderingMode().getDescription() };
}
@Callback(doc = "Set the current shield rendering mode. Values are 'Invisible', 'Shield', or 'Solid'", setter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] setShieldRenderingMode(Context context, Arguments args) throws Exception {
String mode = args.checkString(0);
return setShieldRenderingMode(mode);
}
private Object[] setShieldRenderingMode(String mode) {
ShieldRenderingMode renderingMode = ShieldRenderingMode.getMode(mode);
if (renderingMode == null) {
throw new IllegalArgumentException("Not a valid mode");
}
setShieldRenderingMode(renderingMode);
return null;
}
@Callback(doc = "Return true if the shield is active", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] isShieldActive(Context context, Arguments args) throws Exception {
return new Object[] { isShieldActive() };
}
@Callback(doc = "Return true if the shield is composed (i.e. formed)", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] isShieldComposed(Context context, Arguments args) throws Exception {
return new Object[] { isShieldComposed() };
}
@Callback(doc = "Form the shield (compose it)")
@Optional.Method(modid = "OpenComputers")
public Object[] composeShield(Context context, Arguments args) throws Exception {
return composeShieldComp(false);
}
@Callback(doc = "Form the shield (compose it). This version works in disconnected mode (template blocks will connect on corners too)")
@Optional.Method(modid = "OpenComputers")
public Object[] composeShieldDsc(Context context, Arguments args) throws Exception {
return composeShieldComp(true);
}
private Object[] composeShieldComp(boolean ctrl) {
boolean done = false;
if (!isShieldComposed()) {
composeShield(ctrl);
done = true;
}
return new Object[] { done };
}
@Callback(doc = "Break down the shield (decompose it)")
@Optional.Method(modid = "OpenComputers")
public Object[] decomposeShield(Context context, Arguments args) throws Exception {
return decomposeShieldComp();
}
private Object[] decomposeShieldComp() {
boolean done = false;
if (isShieldComposed()) {
decomposeShield();
done = true;
}
return new Object[] { done };
}
public List<ShieldFilter> getFilters() {
return filters;
}
public int getShieldColor() {
return shieldColor;
}
public void setShieldColor(int shieldColor) {
this.shieldColor = shieldColor;
updateShield();
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private void delFilter(int selected) {
filters.remove(selected);
updateShield();
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private void upFilter(int selected) {
ShieldFilter filter1 = filters.get(selected-1);
ShieldFilter filter2 = filters.get(selected);
filters.set(selected - 1, filter2);
filters.set(selected, filter1);
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private void downFilter(int selected) {
ShieldFilter filter1 = filters.get(selected);
ShieldFilter filter2 = filters.get(selected+1);
filters.set(selected, filter2);
filters.set(selected + 1, filter1);
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private void addFilter(int action, String type, String player, int selected) {
ShieldFilter filter = AbstractShieldFilter.createFilter(type);
filter.setAction(action);
if (filter instanceof PlayerFilter) {
((PlayerFilter)filter).setName(player);
}
if (selected == -1) {
filters.add(filter);
} else {
filters.add(selected, filter);
}
updateShield();
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
public RedstoneMode getRedstoneMode() {
return redstoneMode;
}
public void setRedstoneMode(RedstoneMode redstoneMode) {
this.redstoneMode = redstoneMode;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
}
public DamageTypeMode getDamageMode() {
return damageMode;
}
public void setDamageMode(DamageTypeMode damageMode) {
this.damageMode = damageMode;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
markDirty();
}
public ShieldRenderingMode getShieldRenderingMode() {
return shieldRenderingMode;
}
public void setShieldRenderingMode(ShieldRenderingMode shieldRenderingMode) {
this.shieldRenderingMode = shieldRenderingMode;
if (shieldComposed) {
updateShield();
}
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private int[] calculateCamoId() {
ItemStack stack = stacks[0];
int camoId = -1;
int meta = 0;
int te = 0;
if (ShieldRenderingMode.MODE_SOLID.equals(shieldRenderingMode) && stack != null && stack.getItem() != null) {
if (!(stack.getItem() instanceof ItemBlock)) {
return new int[] { camoId, meta, te };
}
Block block = ((ItemBlock) stack.getItem()).field_150939_a;
camoId = Block.getIdFromBlock(block);
meta = stack.getItemDamage();
if (block.hasTileEntity(meta)) {
te = 1;
}
}
return new int[] { camoId, meta, te };
}
private Block calculateShieldBlock(int damageBits) {
if (!shieldActive || powerTimeout > 0) {
return Blocks.air;
}
if (ShieldRenderingMode.MODE_INVISIBLE.equals(shieldRenderingMode)) {
if (damageBits == 0) {
return ShieldSetup.noTickInvisibleShieldBlock;
} else {
return ShieldSetup.invisibleShieldBlock;
}
}
if (damageBits == 0) {
return ShieldSetup.noTickSolidShieldBlock;
} else {
return ShieldSetup.solidShieldBlock;
}
}
private int calculateDamageBits() {
int bits = 0;
for (ShieldFilter filter : filters) {
if ((filter.getAction() & ShieldFilter.ACTION_DAMAGE) != 0) {
if (ItemFilter.ITEM.equals(filter.getFilterName())) {
bits |= AbstractShieldBlock.META_ITEMS;
} else if (AnimalFilter.ANIMAL.equals(filter.getFilterName())) {
bits |= AbstractShieldBlock.META_PASSIVE;
} else if (HostileFilter.HOSTILE.equals(filter.getFilterName())) {
bits |= AbstractShieldBlock.META_HOSTILE;
} else if (PlayerFilter.PLAYER.equals(filter.getFilterName())) {
bits |= AbstractShieldBlock.META_PLAYERS;
} else if (DefaultFilter.DEFAULT.equals(filter.getFilterName())) {
bits |= AbstractShieldBlock.META_ITEMS | AbstractShieldBlock.META_PASSIVE | AbstractShieldBlock.META_HOSTILE | AbstractShieldBlock.META_PLAYERS;
}
}
}
return bits;
}
private int calculateShieldCollisionData() {
int cd = 0;
for (ShieldFilter filter : filters) {
if ((filter.getAction() & ShieldFilter.ACTION_SOLID) != 0) {
if (ItemFilter.ITEM.equals(filter.getFilterName())) {
cd |= AbstractShieldBlock.META_ITEMS;
} else if (AnimalFilter.ANIMAL.equals(filter.getFilterName())) {
cd |= AbstractShieldBlock.META_PASSIVE;
} else if (HostileFilter.HOSTILE.equals(filter.getFilterName())) {
cd |= AbstractShieldBlock.META_HOSTILE;
} else if (PlayerFilter.PLAYER.equals(filter.getFilterName())) {
cd |= AbstractShieldBlock.META_PLAYERS;
} else if (DefaultFilter.DEFAULT.equals(filter.getFilterName())) {
cd |= AbstractShieldBlock.META_ITEMS | AbstractShieldBlock.META_PASSIVE | AbstractShieldBlock.META_HOSTILE | AbstractShieldBlock.META_PLAYERS;
}
}
}
return cd;
}
private int calculateRfPerTick() {
if (!shieldActive) {
return 0;
}
int s = shieldBlocks.size() - 50;
if (s < 10) {
s = 10;
}
int rf = ShieldConfiguration.rfBase * s / 10;
if (ShieldRenderingMode.MODE_SHIELD.equals(shieldRenderingMode)) {
rf += ShieldConfiguration.rfShield * s / 10;
} else if (ShieldRenderingMode.MODE_SOLID.equals(shieldRenderingMode)) {
rf += ShieldConfiguration.rfCamo * s / 10;
}
return rf;
}
public boolean isShieldComposed() {
return shieldComposed;
}
public boolean isShieldActive() {
return shieldActive;
}
public void applyDamageToEntity(Entity entity) {
DamageSource source;
int rf;
if (DamageTypeMode.DAMAGETYPE_GENERIC.equals(damageMode)) {
rf = ShieldConfiguration.rfDamage;
source = DamageSource.generic;
} else {
rf = ShieldConfiguration.rfDamagePlayer;
FakePlayer fakePlayer = FakePlayerFactory.getMinecraft(DimensionManager.getWorld(0));
source = DamageSource.causePlayerDamage(fakePlayer);
}
rf = (int) (rf * costFactor * (4.0f - getInfusedFactor()) / 4.0f);
if (getEnergyStored(ForgeDirection.DOWN) < rf) {
// Not enough RF to do damage.
return;
}
consumeEnergy(rf);
float damage = ShieldConfiguration.damage;
damage *= damageFactor;
damage = damage * (1.0f + getInfusedFactor() / 2.0f);
entity.attackEntityFrom(source, damage);
}
@Override
protected void checkStateServer() {
super.checkStateServer();
boolean checkPower = false;
if (powerTimeout > 0) {
powerTimeout--;
markDirty();
if (powerTimeout > 0) {
return;
} else {
checkPower = true;
}
}
boolean needsUpdate = false;
int rf = calculateRfPerTick();
rf = (int) (rf * (2.0f - getInfusedFactor()) / 2.0f);
if (rf > 0) {
if (getEnergyStored(ForgeDirection.DOWN) < rf) {
powerTimeout = 100; // Wait 5 seconds before trying again.
needsUpdate = true;
} else {
if (checkPower) {
needsUpdate = true;
}
consumeEnergy(rf);
}
}
boolean newShieldActive = shieldActive;
if (redstoneMode == RedstoneMode.REDSTONE_IGNORED) {
newShieldActive = true; // Always active in this mode.
} else {
int meta = worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
boolean rs = BlockTools.getRedstoneSignal(meta);
if (redstoneMode == RedstoneMode.REDSTONE_OFFREQUIRED) {
newShieldActive = !rs;
} else if (redstoneMode == RedstoneMode.REDSTONE_ONREQUIRED) {
newShieldActive = rs;
}
}
if (newShieldActive != shieldActive) {
needsUpdate = true;
shieldActive = newShieldActive;
}
if (needsUpdate) {
updateShield();
markDirty();
}
}
public void composeDecomposeShield(boolean ctrl) {
if (shieldComposed) {
// Shield is already composed. Break it into template blocks again.
decomposeShield();
} else {
// Shield is not composed. Find all nearby template blocks and form a shield.
composeShield(ctrl);
}
}
public void composeShield(boolean ctrl) {
shieldBlocks.clear();
Collection<Coordinate> coordinates;
if (isShapedShield()) {
// Special shaped mode.
ShapeCardItem.Shape shape = ShapeCardItem.getShape(stacks[ShieldContainer.SLOT_SHAPE]);
Coordinate dimension = ShapeCardItem.getClampedDimension(stacks[ShieldContainer.SLOT_SHAPE], ShieldConfiguration.maxShieldDimension);
Coordinate offset = ShapeCardItem.getClampedOffset(stacks[ShieldContainer.SLOT_SHAPE], ShieldConfiguration.maxShieldOffset);
coordinates = new ArrayList<Coordinate>();
ShapeCardItem.composeShape(shape, worldObj, getCoordinate(), dimension, offset, coordinates, supportedBlocks, false, null);
} else {
templateMeta = findTemplateMeta();
coordinates = new HashSet<Coordinate>();
findTemplateBlocks((Set<Coordinate>)coordinates, templateMeta, ctrl, getCoordinate());
}
for (Coordinate c : coordinates) {
shieldBlocks.add(new RelCoordinate(c.getX() - xCoord, c.getY() - yCoord, c.getZ() - zCoord));
}
shieldComposed = true;
updateShield();
}
private boolean isShapedShield() {
return stacks[ShieldContainer.SLOT_SHAPE] != null;
}
private int findTemplateMeta() {
int meta = -1;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
int xx = xCoord + dir.offsetX;
int yy = yCoord + dir.offsetY;
int zz = zCoord + dir.offsetZ;
if (yy >= 0 && yy < worldObj.getHeight()) {
if (ShieldSetup.shieldTemplateBlock.equals(worldObj.getBlock(xx, yy, zz))) {
meta = worldObj.getBlockMetadata(xx, yy, zz);
break;
}
}
}
return meta;
}
@Override
public void selectBlock(EntityPlayer player, int x, int y, int z) {
if (!shieldComposed) {
Logging.message(player, EnumChatFormatting.YELLOW + "Shield is not composed. Nothing happens!");
return;
}
float squaredDistance = getCoordinate().squaredDistance(x, y, z);
if (squaredDistance > ShieldConfiguration.maxDisjointShieldDistance * ShieldConfiguration.maxDisjointShieldDistance) {
Logging.message(player, EnumChatFormatting.YELLOW + "This template is too far to connect to the shield!");
return;
}
Block origBlock = worldObj.getBlock(x, y, z);
Coordinate c = new Coordinate(x, y, z);
if (origBlock == ShieldSetup.shieldTemplateBlock) {
if (isShapedShield()) {
Logging.message(player, EnumChatFormatting.YELLOW + "You cannot add template blocks to a shaped shield (using a shape card)!");
return;
}
Set<Coordinate> templateBlocks = new HashSet<Coordinate>();
findTemplateBlocks(templateBlocks, worldObj.getBlockMetadata(x, y, z), false, c);
int[] camoId = calculateCamoId();
int cddata = calculateShieldCollisionData();
int damageBits = calculateDamageBits();
Block block = calculateShieldBlock(damageBits);
for (Coordinate templateBlock : templateBlocks) {
RelCoordinate relc = new RelCoordinate(templateBlock.getX() - xCoord, templateBlock.getY() - yCoord, templateBlock.getZ() - zCoord);
shieldBlocks.add(relc);
updateShieldBlock(camoId, cddata, damageBits, block, relc);
}
} else if (origBlock instanceof AbstractShieldBlock) {
shieldBlocks.remove(new RelCoordinate(c.getX() - xCoord, c.getY() - yCoord, c.getZ() - zCoord));
if (isShapedShield()) {
worldObj.setBlockToAir(x, y, z);
} else {
worldObj.setBlock(x, y, z, ShieldSetup.shieldTemplateBlock, templateMeta, 2);
}
} else {
Logging.message(player, EnumChatFormatting.YELLOW + "The selected shield can't do anything with this block!");
return;
}
markDirty();
notifyBlockUpdate();
}
/**
* Update all shield blocks. Possibly creating the shield.
*/
private void updateShield() {
int[] camoId = calculateCamoId();
int cddata = calculateShieldCollisionData();
int damageBits = calculateDamageBits();
Block block = calculateShieldBlock(damageBits);
for (RelCoordinate c : shieldBlocks) {
if (Blocks.air.equals(block)) {
worldObj.setBlockToAir(xCoord + c.getDx(), yCoord + c.getDy(), zCoord + c.getDz());
} else {
updateShieldBlock(camoId, cddata, damageBits, block, c);
}
}
markDirty();
notifyBlockUpdate();
}
private void updateShieldBlock(int[] camoId, int cddata, int damageBits, Block block, RelCoordinate c) {
worldObj.setBlock(xCoord + c.getDx(), yCoord + c.getDy(), zCoord + c.getDz(), block, camoId[1], 2);
TileEntity te = worldObj.getTileEntity(xCoord + c.getDx(), yCoord + c.getDy(), zCoord + c.getDz());
if (te instanceof ShieldBlockTileEntity) {
ShieldBlockTileEntity shieldBlockTileEntity = (ShieldBlockTileEntity) te;
shieldBlockTileEntity.setCamoBlock(camoId[0], camoId[2]);
shieldBlockTileEntity.setShieldBlock(getCoordinate());
shieldBlockTileEntity.setDamageBits(damageBits);
shieldBlockTileEntity.setCollisionData(cddata);
shieldBlockTileEntity.setShieldColor(shieldColor);
}
}
public void decomposeShield() {
for (RelCoordinate c : shieldBlocks) {
int cx = xCoord + c.getDx();
int cy = yCoord + c.getDy();
int cz = zCoord + c.getDz();
Block block = worldObj.getBlock(cx, cy, cz);
if (worldObj.isAirBlock(cx, cy, cz) || block instanceof AbstractShieldBlock) {
if (isShapedShield()) {
worldObj.setBlockToAir(cx, cy, cz);
} else {
worldObj.setBlock(cx, cy, cz, ShieldSetup.shieldTemplateBlock, templateMeta, 2);
}
} else {
if (!isShapedShield()) {
// No room, just spawn the block
BlockTools.spawnItemStack(worldObj, cx, cy, cz, new ItemStack(ShieldSetup.shieldTemplateBlock, 1, templateMeta));
}
}
}
shieldComposed = false;
shieldActive = false;
shieldBlocks.clear();
markDirty();
notifyBlockUpdate();
}
/**
* Find all template blocks recursively.
* @param coordinateSet the set with coordinates to update during the search
* @param meta the metavalue for the shield template block we support
* @param ctrl if true also scan for blocks in corners
*/
private void findTemplateBlocks(Set<Coordinate> coordinateSet, int meta, boolean ctrl, Coordinate start) {
Deque<Coordinate> todo = new ArrayDeque<Coordinate>();
if (ctrl) {
addToTodoCornered(coordinateSet, todo, start, meta);
while (!todo.isEmpty() && coordinateSet.size() < supportedBlocks) {
Coordinate coordinate = todo.pollFirst();
coordinateSet.add(coordinate);
addToTodoCornered(coordinateSet, todo, coordinate, meta);
}
} else {
addToTodoStraight(coordinateSet, todo, start, meta);
while (!todo.isEmpty() && coordinateSet.size() < supportedBlocks) {
Coordinate coordinate = todo.pollFirst();
coordinateSet.add(coordinate);
addToTodoStraight(coordinateSet, todo, coordinate, meta);
}
}
}
private void addToTodoStraight(Set<Coordinate> coordinateSet, Deque<Coordinate> todo, Coordinate coordinate, int meta) {
int x = coordinate.getX();
int y = coordinate.getY();
int z = coordinate.getZ();
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
int xx = x + dir.offsetX;
int yy = y + dir.offsetY;
int zz = z + dir.offsetZ;
if (yy >= 0 && yy < worldObj.getHeight()) {
Coordinate c = new Coordinate(xx, yy, zz);
if (!coordinateSet.contains(c)) {
if (ShieldSetup.shieldTemplateBlock.equals(worldObj.getBlock(xx, yy, zz))) {
int m = worldObj.getBlockMetadata(xx, yy, zz);
if (m == meta) {
if (!todo.contains(c)) {
todo.addLast(c);
}
}
}
}
}
}
}
private void addToTodoCornered(Set<Coordinate> coordinateSet, Deque<Coordinate> todo, Coordinate coordinate, int meta) {
int x = coordinate.getX();
int y = coordinate.getY();
int z = coordinate.getZ();
for (int xx = x-1 ; xx <= x+1 ; xx++) {
for (int yy = y-1 ; yy <= y+1 ; yy++) {
for (int zz = z-1 ; zz <= z+1 ; zz++) {
if (xx != x || yy != y || zz != z) {
if (yy >= 0 && yy < worldObj.getHeight()) {
Coordinate c = new Coordinate(xx, yy, zz);
if (!coordinateSet.contains(c)) {
if (ShieldSetup.shieldTemplateBlock.equals(worldObj.getBlock(xx, yy, zz))) {
int m = worldObj.getBlockMetadata(xx, yy, zz);
if (m == meta) {
if (!todo.contains(c)) {
todo.addLast(c);
}
}
}
}
}
}
}
}
}
}
private static short bytesToShort(byte b1, byte b2) {
short s1 = (short) (b1 & 0xff);
short s2 = (short) (b2 & 0xff);
return (short) (s1 * 256 + s2);
}
private static byte shortToByte1(short s) {
return (byte) ((s & 0xff00) >> 8);
}
private static byte shortToByte2(short s) {
return (byte) (s & 0xff);
}
@Override
public void readFromNBT(NBTTagCompound tagCompound) {
super.readFromNBT(tagCompound);
shieldComposed = tagCompound.getBoolean("composed");
shieldActive = tagCompound.getBoolean("active");
powerTimeout = tagCompound.getInteger("powerTimeout");
templateMeta = tagCompound.getInteger("templateMeta");
shieldBlocks.clear();;
if (tagCompound.hasKey("coordinates")) {
// Support for legacy coordinates field
NBTTagCompound compound = tagCompound.getCompoundTag("coordinates");
NBTTagList list = compound.getTagList("list", 10);
for(int i = 0; i < list.tagCount(); ++i) {
Coordinate c = Coordinate.readFromNBT(list.getCompoundTagAt(i), "c");
shieldBlocks.add(new RelCoordinate(c.getX() - xCoord, c.getY() - yCoord, c.getZ() - zCoord));
}
} else {
byte[] byteArray = tagCompound.getByteArray("relcoords");
int j = 0;
for (int i = 0 ; i < byteArray.length / 6 ; i++) {
short dx = bytesToShort(byteArray[j+0], byteArray[j+1]);
short dy = bytesToShort(byteArray[j+2], byteArray[j+3]);
short dz = bytesToShort(byteArray[j+4], byteArray[j+5]);
j += 6;
shieldBlocks.add(new RelCoordinate(dx, dy, dz));
}
}
}
@Override
public void readRestorableFromNBT(NBTTagCompound tagCompound) {
super.readRestorableFromNBT(tagCompound);
readBufferFromNBT(tagCompound);
int m = tagCompound.getInteger("visMode");
shieldRenderingMode = ShieldRenderingMode.values()[m];
m = tagCompound.getByte("rsMode");
redstoneMode = RedstoneMode.values()[m];
m = tagCompound.getByte("damageMode");
damageMode = DamageTypeMode.values()[m];
camoRenderPass = tagCompound.getInteger("camoRenderPass");
shieldColor = tagCompound.getInteger("shieldColor");
if (shieldColor == 0) {
shieldColor = 0x96ffc8;
}
readFiltersFromNBT(tagCompound);
}
private void readFiltersFromNBT(NBTTagCompound tagCompound) {
filters.clear();
NBTTagList filterList = tagCompound.getTagList("filters", Constants.NBT.TAG_COMPOUND);
if (filterList != null) {
for (int i = 0 ; i < filterList.tagCount() ; i++) {
NBTTagCompound compound = filterList.getCompoundTagAt(i);
filters.add(AbstractShieldFilter.createFilter(compound));
}
}
}
private void readBufferFromNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = tagCompound.getTagList("Items", Constants.NBT.TAG_COMPOUND);
for (int i = 0 ; i < bufferTagList.tagCount() ; i++) {
NBTTagCompound nbtTagCompound = bufferTagList.getCompoundTagAt(i);
stacks[i+ShieldContainer.SLOT_BUFFER] = ItemStack.loadItemStackFromNBT(nbtTagCompound);
}
}
@Override
public void writeToNBT(NBTTagCompound tagCompound) {
super.writeToNBT(tagCompound);
tagCompound.setBoolean("composed", shieldComposed);
tagCompound.setBoolean("active", shieldActive);
tagCompound.setInteger("powerTimeout", powerTimeout);
tagCompound.setInteger("templateMeta", templateMeta);
byte[] blocks = new byte[shieldBlocks.size() * 6];
int j = 0;
for (RelCoordinate c : shieldBlocks) {
blocks[j+0] = shortToByte1((short) c.getDx());
blocks[j+1] = shortToByte2((short) c.getDx());
blocks[j+2] = shortToByte1((short) c.getDy());
blocks[j+3] = shortToByte2((short) c.getDy());
blocks[j+4] = shortToByte1((short) c.getDz());
blocks[j+5] = shortToByte2((short) c.getDz());
j += 6;
}
tagCompound.setByteArray("relcoords", blocks);
// shieldBlocksOld.writeToNBT(tagCompound, "coordinates");
}
@Override
public void writeRestorableToNBT(NBTTagCompound tagCompound) {
super.writeRestorableToNBT(tagCompound);
writeBufferToNBT(tagCompound);
tagCompound.setInteger("visMode", shieldRenderingMode.ordinal());
tagCompound.setByte("rsMode", (byte) redstoneMode.ordinal());
tagCompound.setByte("damageMode", (byte) damageMode.ordinal());
tagCompound.setInteger("camoRenderPass", camoRenderPass);
tagCompound.setInteger("shieldColor", shieldColor);
writeFiltersToNBT(tagCompound);
}
private void writeFiltersToNBT(NBTTagCompound tagCompound) {
NBTTagList filterList = new NBTTagList();
for (ShieldFilter filter : filters) {
NBTTagCompound compound = new NBTTagCompound();
filter.writeToNBT(compound);
filterList.appendTag(compound);
}
tagCompound.setTag("filters", filterList);
}
private void writeBufferToNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = new NBTTagList();
for (ItemStack stack : stacks) {
NBTTagCompound nbtTagCompound = new NBTTagCompound();
if (stack != null) {
stack.writeToNBT(nbtTagCompound);
}
bufferTagList.appendTag(nbtTagCompound);
}
tagCompound.setTag("Items", bufferTagList);
}
@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_SHIELDVISMODE.equals(command)) {
String m = args.get("mode").getString();
setShieldRenderingMode(ShieldRenderingMode.getMode(m));
return true;
} else if (CMD_APPLYCAMO.equals(command)) {
camoRenderPass = args.get("pass").getInteger();
updateShield();
return true;
} else if (CMD_ADDFILTER.equals(command)) {
int action = args.get("action").getInteger();
String type = args.get("type").getString();
String player = args.get("player").getString();
int selected = args.get("selected").getInteger();
addFilter(action, type, player, selected);
return true;
} else if (CMD_DELFILTER.equals(command)) {
int selected = args.get("selected").getInteger();
delFilter(selected);
return true;
} else if (CMD_UPFILTER.equals(command)) {
int selected = args.get("selected").getInteger();
upFilter(selected);
return true;
} else if (CMD_DOWNFILTER.equals(command)) {
int selected = args.get("selected").getInteger();
downFilter(selected);
return true;
} else if (CMD_RSMODE.equals(command)) {
String m = args.get("rs").getString();
setRedstoneMode(RedstoneMode.getMode(m));
return true;
} else if (CMD_DAMAGEMODE.equals(command)) {
String m = args.get("mode").getString();
setDamageMode(DamageTypeMode.getMode(m));
return true;
} else if (CMD_SETCOLOR.equals(command)) {
int color = args.get("color").getInteger();
setShieldColor(color);
return true;
}
return false;
}
@Override
public List executeWithResultList(String command, Map<String, Argument> args) {
List rc = super.executeWithResultList(command, args);
if (rc != null) {
return rc;
}
if (CMD_GETFILTERS.equals(command)) {
return getFilters();
}
return null;
}
@Override
public boolean execute(String command, List list) {
boolean rc = super.execute(command, list);
if (rc) {
return true;
}
if (CLIENTCMD_GETFILTERS.equals(command)) {
GuiShield.storeFiltersForClient(list);
return true;
}
return false;
}
@Override
public int getSizeInventory() {
return 1;
}
@Override
public ItemStack getStackInSlot(int index) {
return stacks[index];
}
@Override
public ItemStack decrStackSize(int index, int amount) {
if (index == ShieldContainer.SLOT_SHAPE && stacks[index] != null && amount > 0) {
// Restart if we go from having a stack to not having stack or the other way around.
decomposeShield();
}
if (stacks[index] != null) {
if (stacks[index].stackSize <= amount) {
ItemStack old = stacks[index];
stacks[index] = null;
markDirty();
return old;
}
ItemStack its = stacks[index].splitStack(amount);
if (stacks[index].stackSize == 0) {
stacks[index] = null;
}
markDirty();
return its;
}
return null;
}
@Override
public ItemStack getStackInSlotOnClosing(int index) {
return null;
}
@Override
public void setInventorySlotContents(int index, ItemStack stack) {
if (index == ShieldContainer.SLOT_SHAPE && ((stack == null && stacks[index] != null) || (stack != null && stacks[index] == null))) {
// Restart if we go from having a stack to not having stack or the other way around.
decomposeShield();
}
stacks[index] = stack;
if (stack != null && stack.stackSize > getInventoryStackLimit()) {
stack.stackSize = getInventoryStackLimit();
}
markDirty();
}
@Override
public String getInventoryName() {
return "Shield Inventory";
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return canPlayerAccess(player);
}
@Override
public void openInventory() {
}
@Override
public void closeInventory() {
}
@Override
public boolean isItemValidForSlot(int index, ItemStack stack) {
return true;
}
}