| /** |
| * Copyright (c) SpaceToad, 2011 |
| * http://www.mod-buildcraft.com |
| * |
| * BuildCraft is distributed under the terms of the Minecraft Mod Public |
| * License 1.0, or MMPL. Please check the contents of the license located in |
| * http://www.mod-buildcraft.com/MMPL-1.0.txt |
| */ |
| |
| package buildcraft.api.blueprints; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| |
| import buildcraft.api.core.BuildCraftAPI; |
| |
| import net.minecraft.src.Block; |
| import net.minecraft.src.BlockContainer; |
| import net.minecraft.src.Item; |
| import net.minecraft.src.ItemStack; |
| import net.minecraft.src.TileEntity; |
| |
| /** |
| * This class allow to specify specific behavior for blocks stored in |
| * blueprints: |
| * |
| * - what items needs to be used to create that block - how the block has to be |
| * built on the world - how to rotate the block - what extra data to store / |
| * load in the blueprint |
| * |
| * Default implementations of this can be seen in the package |
| * buildcraft.api.bptblocks. The class BptBlockUtils provide |
| * some additional utilities. |
| * |
| * Blueprints perform "id translation" in case the block ids between a blueprint |
| * and the world installation are different. In order to translate block ids, |
| * blocks needs to be uniquely identified. By default, this identification is |
| * done by: |
| * |
| * - the block simple class name - the tile simple class name (if any) - the |
| * block name |
| * |
| * In certain circumstances, the above is not enough (e.g. if several blocks |
| * share the same class and the same name, with no tile). In this case, |
| * additional data may be provided by children of this class: |
| * |
| * - mod name - custom signature |
| * |
| * At blueprint load time, BuildCraft will check that each block id of the |
| * blueprint corresponds to the block id in the installation. If not, it will |
| * perform a search through the block list, and upon matching signature, it will |
| * translate all blocks ids of the blueprint to the installation ones. If no |
| * such block id is found, BuildCraft will assume that the block is not |
| * installed and will not load the blueprint. |
| */ |
| public class BptBlock { |
| |
| public final int blockId; |
| |
| public BptBlock(int blockId) { |
| this.blockId = blockId; |
| |
| BlueprintManager.blockBptProps[blockId] = this; |
| } |
| |
| /** |
| * Returns the requirements needed to build this block. When the |
| * requirements are met, they will be removed all at once from the builder, |
| * before calling buildBlock. |
| */ |
| public void addRequirements(BptSlotInfo slot, IBptContext context, LinkedList<ItemStack> requirements) { |
| if (slot.blockId != 0) { |
| if (slot.storedRequirements.size() != 0) { |
| requirements.addAll(slot.storedRequirements); |
| } else { |
| requirements.add(new ItemStack(slot.blockId, 1, slot.meta)); |
| } |
| } |
| } |
| |
| /** |
| * This is called each time an item matches a reqquirement, that is: (req id |
| * == stack id) for damageable items (req id == stack id && req dmg == stack |
| * dmg) for other items by default, it will increase damage of damageable |
| * items by the amount of damage of the requirement, and remove the intended |
| * amount of non damageable item. |
| * |
| * Client may override this behavior for default items. Note that this |
| * subprogram may be called twice with the same parameters, once with a copy |
| * of requirements and stack to check if the entire requirements can be |
| * fullfilled, and once with the real inventory. Implementer is responsible |
| * for updating req (with the remaining requirements if any) and stack |
| * (after usage) |
| * |
| * returns: what was used (similer to req, but created from stack, so that any NBT based differences are drawn from the correct source) |
| */ |
| public ItemStack useItem(BptSlotInfo slot, IBptContext context, ItemStack req, ItemStack stack) { |
| ItemStack result = stack.copy(); |
| if (stack.isItemStackDamageable()) { |
| if (req.getItemDamage() + stack.getItemDamage() <= stack.getMaxDamage()) { |
| stack.setItemDamage(req.getItemDamage() + stack.getItemDamage()); |
| result.setItemDamage(req.getItemDamage()); |
| req.stackSize = 0; |
| } |
| |
| if (stack.getItemDamage() >= stack.getMaxDamage()) { |
| stack.stackSize = 0; |
| } |
| } else { |
| if (stack.stackSize >= req.stackSize) { |
| result.stackSize = req.stackSize; |
| stack.stackSize -= req.stackSize; |
| req.stackSize = 0; |
| } else { |
| req.stackSize -= stack.stackSize; |
| stack.stackSize = 0; |
| } |
| } |
| |
| if (stack.stackSize == 0 && stack.getItem().getContainerItem() != null) { |
| Item container = stack.getItem().getContainerItem(); |
| |
| stack.itemID = container.shiftedIndex; |
| stack.stackSize = 1; |
| stack.setItemDamage(0); |
| } |
| return result; |
| } |
| |
| /** |
| * Return true if the block on the world correspond to the block stored in |
| * the blueprint at the location given by the slot. By default, this |
| * subprogram is permissive and doesn't take into account metadata. |
| * |
| * Added metadata sensitivity //Krapht |
| */ |
| public boolean isValid(BptSlotInfo slot, IBptContext context) { |
| return slot.blockId == context.world().getBlockId(slot.x, slot.y, slot.z) |
| && slot.meta == context.world().getBlockMetadata(slot.x, slot.y, slot.z); |
| } |
| |
| /** |
| * Perform a 90 degree rotation to the slot. |
| */ |
| public void rotateLeft(BptSlotInfo slot, IBptContext context) { |
| |
| } |
| |
| /** |
| * Places the block in the world, at the location specified in the slot. |
| */ |
| public void buildBlock(BptSlotInfo slot, IBptContext context) { |
| // Meta needs to be specified twice, depending on the block behavior |
| context.world().setBlockAndMetadataWithNotify(slot.x, slot.y, slot.z, slot.blockId, slot.meta); |
| context.world().setBlockMetadataWithNotify(slot.x, slot.y, slot.z, slot.meta); |
| |
| if (Block.blocksList[slot.blockId] instanceof BlockContainer) { |
| TileEntity tile = context.world().getBlockTileEntity(slot.x, slot.y, slot.z); |
| |
| slot.cpt.setInteger("x", slot.x); |
| slot.cpt.setInteger("y", slot.y); |
| slot.cpt.setInteger("z", slot.z); |
| |
| if (tile != null) { |
| tile.readFromNBT(slot.cpt); |
| } |
| } |
| } |
| |
| /** |
| * Return true if the block should not be placed to the world. Requirements |
| * will not be asked on such a block, and building will not be called. |
| */ |
| public boolean ignoreBuilding(BptSlotInfo slot) { |
| return false; |
| } |
| |
| /** |
| * Initializes a slot from the blueprint according to an objet placed on {x, |
| * y, z} on the world. This typically means adding entries in slot.cpt. Note |
| * that "id" and "meta" will be set automatically, corresponding to the |
| * block id and meta. |
| * |
| * By default, if the block is a BlockContainer, tile information will be to |
| * save / load the block. |
| */ |
| public void initializeFromWorld(BptSlotInfo slot, IBptContext context, int x, int y, int z) { |
| if (Block.blocksList[slot.blockId] instanceof BlockContainer) { |
| TileEntity tile = context.world().getBlockTileEntity(x, y, z); |
| |
| if (tile != null) { |
| tile.writeToNBT(slot.cpt); |
| } |
| } |
| |
| if (Block.blocksList[slot.blockId] != null) { |
| ArrayList<ItemStack> req = Block.blocksList[slot.blockId].getBlockDropped(context.world(), x, y, z, context.world() |
| .getBlockMetadata(x, y, z), 0); |
| |
| if (req != null) { |
| slot.storedRequirements.addAll(req); |
| } |
| } |
| } |
| |
| /** |
| * Called on a block when the blueprint has finished to place all the |
| * blocks. This may be useful to adjust variable depending on surrounding |
| * blocks that may not be there already at initial building. |
| */ |
| public void postProcessing(BptSlotInfo slot, IBptContext context) { |
| |
| } |
| |
| /** |
| * By default, block class name, block tile name and block name are used to |
| * define block signature. Overriding this subprogram may allow to replace |
| * some of these with stars, specify the mod that this block kind is coming |
| * from or add custom data to the signature. |
| */ |
| public BlockSignature getSignature(Block block) { |
| BlockSignature sig = new BlockSignature(); |
| |
| if (block.blockID > BuildCraftAPI.LAST_ORIGINAL_BLOCK) { |
| sig.blockClassName = block.getClass().getSimpleName(); |
| |
| if (block instanceof BlockContainer) { |
| //TODO: Try to see if we can get a world instance to call with instead of null |
| TileEntity tile = ((BlockContainer) block).createNewTileEntity(null); |
| |
| if (tile != null) { |
| sig.tileClassName = tile.getClass().getSimpleName(); |
| } |
| } |
| } |
| |
| sig.blockName = block.getBlockName(); |
| sig.replaceNullWithStar(); |
| |
| return sig; |
| } |
| |
| /** |
| * By default, block name, block and tile classes, mod name and custom |
| * signature are matched to verify if a blueprint block corresponds to the |
| * installation block - except for the default blocks which don't check for |
| * classes. For any value, * means match with anything. For compatibilty and |
| * evolution reasons, mods may want to write a different policy, allowing to |
| * migrate one format to the other. |
| */ |
| public boolean match(Block block, BlockSignature sig) { |
| if (block == null) { |
| return false; |
| } |
| |
| BlockSignature inst = BlueprintManager.getBlockSignature(block); |
| |
| return starMatch(sig.blockName, inst.blockName) && starMatch(sig.blockClassName, inst.blockClassName) |
| && starMatch(sig.tileClassName, inst.tileClassName) && starMatch(sig.customField, inst.customField) |
| && starMatch(sig.mod, inst.mod); |
| } |
| |
| private boolean starMatch(String s1, String s2) { |
| return s1.equals("*") || s2.equals("*") || s1.equals(s2); |
| } |
| } |