package net.geforcemods.securitycraft.api; import java.util.ArrayList; import java.util.Iterator; import net.geforcemods.securitycraft.items.ItemModule; import net.geforcemods.securitycraft.misc.EnumCustomModules; import net.geforcemods.securitycraft.tileentity.TileEntityOwnable; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import net.minecraftforge.common.util.Constants; /** * Extend this class in your TileEntity to make it customizable. You will * be able to modify it with the various modules in SecurityCraft, and * have your block do different functions based on what modules are * inserted. * * @author Geforce */ public abstract class CustomizableSCTE extends TileEntityOwnable implements IInventory{ private boolean linkable = false; public ArrayList<LinkedBlock> linkedBlocks = new ArrayList<LinkedBlock>(); private NBTTagList nbtTagStorage = null; public ItemStack[] itemStacks = new ItemStack[getNumberOfCustomizableOptions()]; public void update() { super.update(); if(hasWorldObj() && nbtTagStorage != null) { readLinkedBlocks(nbtTagStorage); sync(); nbtTagStorage = null; } } public void readFromNBT(NBTTagCompound par1NBTTagCompound) { super.readFromNBT(par1NBTTagCompound); NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Modules", 10); this.itemStacks = new ItemStack[getNumberOfCustomizableOptions()]; for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(i); byte b0 = nbttagcompound1.getByte("ModuleSlot"); if (b0 >= 0 && b0 < this.itemStacks.length) { this.itemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } if(customOptions() != null) { for(Option<?> option : customOptions()) { option.readFromNBT(par1NBTTagCompound); } } if (par1NBTTagCompound.hasKey("linkable")) { this.linkable = par1NBTTagCompound.getBoolean("linkable"); } if (linkable && par1NBTTagCompound.hasKey("linkedBlocks")) { if(!hasWorldObj()) { nbtTagStorage = par1NBTTagCompound.getTagList("linkedBlocks", Constants.NBT.TAG_COMPOUND); return; } readLinkedBlocks(par1NBTTagCompound.getTagList("linkedBlocks", Constants.NBT.TAG_COMPOUND)); } } public void writeToNBT(NBTTagCompound par1NBTTagCompound) { super.writeToNBT(par1NBTTagCompound); NBTTagList nbttaglist = new NBTTagList(); for(int i = 0; i < this.itemStacks.length; i++){ if (this.itemStacks[i] != null) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("ModuleSlot", (byte)i); this.itemStacks[i].writeToNBT(nbttagcompound1); nbttaglist.appendTag(nbttagcompound1); } } par1NBTTagCompound.setTag("Modules", nbttaglist); if(customOptions() != null) { for(Option<?> option : customOptions()) { option.writeToNBT(par1NBTTagCompound); } } par1NBTTagCompound.setBoolean("linkable", linkable); if(linkable && hasWorldObj() && linkedBlocks.size() > 0) { NBTTagList tagList = new NBTTagList(); Iterator<LinkedBlock> iterator = linkedBlocks.iterator(); while(iterator.hasNext()) { LinkedBlock block = iterator.next(); NBTTagCompound tag = new NBTTagCompound(); if(block != null) { if(!block.validate(worldObj)) { linkedBlocks.remove(block); continue; } tag.setString("blockName", block.blockName); tag.setInteger("blockX", block.getX()); tag.setInteger("blockY", block.getY()); tag.setInteger("blockZ", block.getZ()); } tagList.appendTag(tag); } par1NBTTagCompound.setTag("linkedBlocks", tagList); } } private void readLinkedBlocks(NBTTagList list) { if(!linkable) return; for(int i = 0; i < list.tagCount(); i++) { String name = list.getCompoundTagAt(i).getString("blockName"); int x = list.getCompoundTagAt(i).getInteger("blockX"); int y = list.getCompoundTagAt(i).getInteger("blockY"); int z = list.getCompoundTagAt(i).getInteger("blockZ"); LinkedBlock block = new LinkedBlock(name, x, y, z); if(hasWorldObj() && !block.validate(worldObj)) { list.removeTag(i); continue; } if(!linkedBlocks.contains(block)){ link(this, block.asTileEntity(worldObj)); } } } public int getSizeInventory() { return getNumberOfCustomizableOptions(); } public ItemStack getStackInSlot(int par1) { return this.itemStacks[par1]; } public ItemStack decrStackSize(int par1, int par2) { if (this.itemStacks[par1] != null) { ItemStack itemstack; if (this.itemStacks[par1].stackSize <= par2) { itemstack = this.itemStacks[par1]; this.itemStacks[par1] = null; this.onModuleRemoved(itemstack, ((ItemModule) itemstack.getItem()).getModule()); createLinkedBlockAction(EnumLinkedAction.MODULE_REMOVED, new Object[]{ itemstack, ((ItemModule) itemstack.getItem()).getModule() }, this); return itemstack; } else { itemstack = this.itemStacks[par1].splitStack(par2); if (this.itemStacks[par1].stackSize == 0) { this.itemStacks[par1] = null; } this.onModuleRemoved(itemstack, ((ItemModule) itemstack.getItem()).getModule()); createLinkedBlockAction(EnumLinkedAction.MODULE_REMOVED, new Object[]{ itemstack, ((ItemModule) itemstack.getItem()).getModule() }, this); return itemstack; } } else { return null; } } /** * Copy of decrStackSize which can't be overrided by subclasses. */ public ItemStack safeDecrStackSize(int par1, int par2) { if (this.itemStacks[par1] != null) { ItemStack itemstack; if (this.itemStacks[par1].stackSize <= par2) { itemstack = this.itemStacks[par1]; this.itemStacks[par1] = null; this.onModuleRemoved(itemstack, ((ItemModule) itemstack.getItem()).getModule()); createLinkedBlockAction(EnumLinkedAction.MODULE_REMOVED, new Object[]{ itemstack, ((ItemModule) itemstack.getItem()).getModule() }, this); return itemstack; } else { itemstack = this.itemStacks[par1].splitStack(par2); if (this.itemStacks[par1].stackSize == 0) { this.itemStacks[par1] = null; } this.onModuleRemoved(itemstack, ((ItemModule) itemstack.getItem()).getModule()); createLinkedBlockAction(EnumLinkedAction.MODULE_REMOVED, new Object[]{ itemstack, ((ItemModule) itemstack.getItem()).getModule() }, this); return itemstack; } } else { return null; } } public ItemStack getStackInSlotOnClosing(int par1) { if (this.itemStacks[par1] != null) { ItemStack itemstack = this.itemStacks[par1]; this.itemStacks[par1] = null; return itemstack; } else { return null; } } /** * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections). */ public void setInventorySlotContents(int par1, ItemStack par2) { this.itemStacks[par1] = par2; if (par2 != null && par2.stackSize > this.getInventoryStackLimit()) { par2.stackSize = this.getInventoryStackLimit(); } if(par2 != null){ this.onModuleInserted(par2, ((ItemModule) par2.getItem()).getModule()); } } /** * Copy of setInventorySlotContents which can't be overrided by subclasses. */ public void safeSetInventorySlotContents(int par1, ItemStack par2) { this.itemStacks[par1] = par2; if (par2 != null && par2.stackSize > this.getInventoryStackLimit()) { par2.stackSize = this.getInventoryStackLimit(); } if(par2 != null && par2.getItem() != null && par2.getItem() instanceof ItemModule){ this.onModuleInserted(par2, ((ItemModule) par2.getItem()).getModule()); createLinkedBlockAction(EnumLinkedAction.MODULE_INSERTED, new Object[]{ par2, ((ItemModule) par2.getItem()).getModule() }, this); } } public IChatComponent getDisplayName() { return new ChatComponentTranslation(getName()); } public String getName(){ return "Customize"; } public boolean hasCustomName() { return (getCustomName() != null && !getCustomName().matches("name")); } public int getInventoryStackLimit() { return 1; } public boolean isUseableByPlayer(EntityPlayer p_70300_1_) { return true; } public void openInventory(EntityPlayer player) {} public void closeInventory(EntityPlayer player) {} public boolean isItemValidForSlot(int par1, ItemStack par2ItemStack) { return par2ItemStack.getItem() instanceof ItemModule ? true : false; } public int getField(int id) { return 0; } public void setField(int id, int value) { } public int getFieldCount() { return 0; } public void clear() { for(int i = 0; i < itemStacks.length; i++){ itemStacks[i] = null; } } public void onTileEntityDestroyed() { if(linkable) { for(LinkedBlock block : linkedBlocks) { CustomizableSCTE.unlink(block.asTileEntity(worldObj), this); } } } //////////////////////// // MODULE STUFF // //////////////////////// /** * Called whenever a module is inserted into a slot in the "Customize" GUI. * * @param stack The raw ItemStack being inserted. * @param module The EnumCustomModules variant of stack. */ public void onModuleInserted(ItemStack stack, EnumCustomModules module) {} /** * Called whenever a module is removed from a slot in the "Customize" GUI. * * @param stack The raw ItemStack being removed. * @param module The EnumCustomModules variant of stack. */ public void onModuleRemoved(ItemStack stack, EnumCustomModules module) {} /** * @return An ArrayList of all EnumCustomModules currently inserted in the TileEntity. */ public ArrayList<EnumCustomModules> getModules(){ ArrayList<EnumCustomModules> modules = new ArrayList<EnumCustomModules>(); for(ItemStack stack : this.itemStacks){ if(stack != null && stack.getItem() instanceof ItemModule){ modules.add(((ItemModule) stack.getItem()).getModule()); } } return modules; } /** * @return The ItemStack for the given EnumCustomModules type. * If there is no ItemStack for that type, returns null. */ public ItemStack getModule(EnumCustomModules module){ for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] != null && this.itemStacks[i].getItem() instanceof ItemModule && ((ItemModule) this.itemStacks[i].getItem()).getModule() == module){ return this.itemStacks[i]; } } return null; } /** * Inserts a generic copy of the given module type into the Customization inventory. */ public void insertModule(EnumCustomModules module){ for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] != null) { if(this.itemStacks[i].getItem() == module.getItem()) { return; } } } for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] == null && module != null){ this.itemStacks[i] = new ItemStack(module.getItem()); break; }else if(this.itemStacks[i] != null && module == null){ this.itemStacks[i] = null; }else{ continue; } } } /** * Inserts an exact copy of the given item into the Customization inventory. */ public void insertModule(ItemStack module){ if(module == null || !(module.getItem() instanceof ItemModule)){ return; } for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] != null) { if(this.itemStacks[i].getItem() == module.getItem()) { return; } } } for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] == null){ this.itemStacks[i] = module.copy(); break; }else{ continue; } } } /** * Removes the first item with the given module type from the inventory. */ public void removeModule(EnumCustomModules module){ for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] != null && this.itemStacks[i].getItem() instanceof ItemModule && ((ItemModule) this.itemStacks[i].getItem()).getModule() == module){ this.itemStacks[i] = null; } } } /** * Does this inventory contain a item with the given module type? */ public boolean hasModule(EnumCustomModules module){ if(module == null){ for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] == null){ return true; } } }else{ for(int i = 0; i < this.itemStacks.length; i++){ if(this.itemStacks[i] != null && this.itemStacks[i].getItem() instanceof ItemModule && ((ItemModule) this.itemStacks[i].getItem()).getModule() == module){ return true; } } } return false; } public int getNumberOfCustomizableOptions(){ return this.acceptedModules().length; } public ArrayList<EnumCustomModules> getAcceptedModules(){ ArrayList<EnumCustomModules> list = new ArrayList<EnumCustomModules>(); for(EnumCustomModules module : acceptedModules()){ list.add(module); } return list; } /** * Checks to see if this TileEntity has an {@link Option} * with the given name, and if so, returns it. * * @param name Option name * @return The Option */ public Option<?> getOptionByName(String name) { for(Option<?> option : customOptions()) { if(option.getName().matches(name)) { return option; } } return null; } /** * Sets this TileEntity able to be "linked" with other blocks, * and being able to do things between them. Call CustomizableSCTE.link() * to link two blocks together. */ public CustomizableSCTE linkable() { linkable = true; return this; } /** * @return If this TileEntity is able to be linked with. */ public boolean canBeLinkedWith() { return linkable; } /** * Links two blocks together. Calls onLinkedBlockAction() * whenever certain events (found in {@link EnumLinkedAction}) occur. */ public static void link(CustomizableSCTE tileEntity1, CustomizableSCTE tileEntity2) { if(!tileEntity1.linkable || !tileEntity2.linkable) return; if(isLinkedWith(tileEntity1, tileEntity2)) return; LinkedBlock block1 = new LinkedBlock(tileEntity1); LinkedBlock block2 = new LinkedBlock(tileEntity2); if(!tileEntity1.linkedBlocks.contains(block2)) { tileEntity1.linkedBlocks.add(block2); } if(!tileEntity2.linkedBlocks.contains(block1)) { tileEntity2.linkedBlocks.add(block1); } } /** * Unlinks the second TileEntity from the first. * * @param tileEntity1 The TileEntity to unlink from * @param tileEntity2 The TileEntity to unlink */ public static void unlink(CustomizableSCTE tileEntity1, CustomizableSCTE tileEntity2) { if(tileEntity1 == null || tileEntity2 == null) return; if(!tileEntity1.linkable || !tileEntity2.linkable) return; LinkedBlock block = new LinkedBlock(tileEntity2); if(tileEntity1.linkedBlocks.contains(block)) { tileEntity1.linkedBlocks.remove(block); } } /** * @return Are the two blocks linked together? */ public static boolean isLinkedWith(CustomizableSCTE tileEntity1, CustomizableSCTE tileEntity2) { if(!tileEntity1.linkable || !tileEntity2.linkable) return false; return tileEntity1.linkedBlocks.contains(new LinkedBlock(tileEntity2)) && tileEntity2.linkedBlocks.contains(new LinkedBlock(tileEntity1)); } /** * Called whenever an {@link Option} in this TileEntity changes values. * * @param option The changed Option */ public void onOptionChanged(Option<?> option) { createLinkedBlockAction(EnumLinkedAction.OPTION_CHANGED, new Option[]{ option }, this); } /** * Calls onLinkedBlockAction() for every block this TileEntity * is linked to. <p> * * <b>NOTE:</b> Never use this method in onLinkedBlockAction(), * use createLinkedBlockAction(EnumLinkedAction, Object[], ArrayList[CustomizableSCTE] instead. * * @param action The action that occurred * @param parameters Action-specific parameters, see comments in {@link EnumLinkedAction} * @param excludedTE The CustomizableSCTE which called this method, prevents infinite loops. */ public void createLinkedBlockAction(EnumLinkedAction action, Object[] parameters, CustomizableSCTE excludedTE) { ArrayList<CustomizableSCTE> list = new ArrayList<CustomizableSCTE>(); list.add(excludedTE); createLinkedBlockAction(action, parameters, list); } /** * Calls onLinkedBlockAction() for every block this TileEntity * is linked to. * * @param action The action that occurred * @param parameters Action-specific parameters, see comments in {@link EnumLinkedAction} * @param excludedTEs CustomizableSCTEs that shouldn't have onLinkedBlockAction() called on them, * prevents infinite loops. Always add your TileEntity to the list whenever using this method */ public void createLinkedBlockAction(EnumLinkedAction action, Object[] parameters, ArrayList<CustomizableSCTE> excludedTEs) { if(!linkable) return; for(LinkedBlock block : linkedBlocks) { if(excludedTEs.contains(block.asTileEntity(worldObj))) { continue; } else { block.asTileEntity(worldObj).onLinkedBlockAction(action, parameters, excludedTEs); block.asTileEntity(worldObj).sync(); } } } /** * Called whenever certain actions occur in blocks * this TileEntity is linked to. See {@link EnumLinkedAction} * for parameter descriptions. <p> * * @param action The {@link EnumLinkedAction} that occurred * @param parameters Important variables related to the action * @param excludedTEs CustomizableSCTEs that aren't going to have onLinkedBlockAction() called on them, * always add your TileEntity to the list if you're going to call createLinkedBlockAction() in this method to chain-link multiple blocks (i.e: like Laser Blocks) */ protected void onLinkedBlockAction(EnumLinkedAction action, Object[] parameters, ArrayList<CustomizableSCTE> excludedTEs) {} /** * @return An array of what {@link EnumCustomModules} can be inserted * into this TileEntity. */ public abstract EnumCustomModules[] acceptedModules(); /** * @return An array of what custom {@link Option}s this * TileEntity has. */ public abstract Option<?>[] customOptions(); }