diff --git a/src/main/java/cr0s/warpdrive/api/IParticleContainerItem.java b/src/main/java/cr0s/warpdrive/api/IParticleContainerItem.java index 5e4e85b7..5988daf6 100644 --- a/src/main/java/cr0s/warpdrive/api/IParticleContainerItem.java +++ b/src/main/java/cr0s/warpdrive/api/IParticleContainerItem.java @@ -3,6 +3,7 @@ package cr0s.warpdrive.api; import net.minecraft.item.ItemStack; public interface IParticleContainerItem { + ParticleStack getParticle(ItemStack container); int getCapacity(ItemStack container); @@ -10,7 +11,11 @@ public interface IParticleContainerItem { int fill(ItemStack container, ParticleStack resource, boolean doFill); ParticleStack drain(ItemStack container, int maxDrain, boolean doDrain); -} + // called during recipe match to set amount to consume in next call to getContainerItem + void setAmountToConsume(ItemStack container, int amount); - \ No newline at end of file + // called during recipe creation to display 'fake' items in NEI, so our handler takes priority + // NBT changes aren't supported by default, so you need to change item or damage. + ItemStack getFakeVariant(ItemStack container); +} \ No newline at end of file diff --git a/src/main/java/cr0s/warpdrive/api/ParticleStack.java b/src/main/java/cr0s/warpdrive/api/ParticleStack.java index 71a70878..b401b832 100644 --- a/src/main/java/cr0s/warpdrive/api/ParticleStack.java +++ b/src/main/java/cr0s/warpdrive/api/ParticleStack.java @@ -7,9 +7,9 @@ import net.minecraft.nbt.NBTTagCompound; import javax.annotation.Nonnull; public class ParticleStack { - public final Particle particle; - public int amount; - public NBTTagCompound tag; + private final Particle particle; + private int amount; + private NBTTagCompound tag; public ParticleStack(@Nonnull final Particle particle, final int amount) { if (!ParticleRegistry.isParticleRegistered(particle)) { @@ -66,6 +66,14 @@ public class ParticleStack { return particle; } + public final int getAmount() { + return amount; + } + + public final void fill(final int amountAdded) { + amount += amountAdded; + } + public String getLocalizedName() { return this.getParticle().getLocalizedName(); } diff --git a/src/main/java/cr0s/warpdrive/config/RecipeParticleShapedOre.java b/src/main/java/cr0s/warpdrive/config/RecipeParticleShapedOre.java new file mode 100644 index 00000000..b9de53bf --- /dev/null +++ b/src/main/java/cr0s/warpdrive/config/RecipeParticleShapedOre.java @@ -0,0 +1,268 @@ +package cr0s.warpdrive.config; + +import cpw.mods.fml.common.registry.GameRegistry; +import cr0s.warpdrive.api.IParticleContainerItem; +import cr0s.warpdrive.api.ParticleStack; +import net.minecraft.block.Block; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; +import net.minecraftforge.oredict.ShapedOreRecipe; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +// Adds support for IParticleContainerItem ingredients +// Loosely inspired from vanilla ShapedOreRecipe +public class RecipeParticleShapedOre implements IRecipe { + + private static final int MAX_CRAFT_GRID_WIDTH = 3; + private static final int MAX_CRAFT_GRID_HEIGHT = 3; + + private ItemStack itemStackResult = null; + private Object[] itemStackIngredients = null; + private int width = 0; + private int height = 0; + private boolean isMirrored = true; + + public RecipeParticleShapedOre(final Block result, Object... recipe){ this(new ItemStack(result), recipe); } + public RecipeParticleShapedOre(final Item result, Object... recipe){ this(new ItemStack(result), recipe); } + public RecipeParticleShapedOre(final ItemStack result, Object... recipe) { + this.itemStackResult = result.copy(); + + String shape = ""; + int indexRecipe = 0; + + // first parameter is an optional boolean 'mirrored' + if (recipe[indexRecipe] instanceof Boolean) { + isMirrored = (Boolean)recipe[indexRecipe]; + if (recipe[indexRecipe + 1] instanceof Object[]) { + recipe = (Object[])recipe[indexRecipe+1]; + } else { + indexRecipe = 1; + } + } + + // second parameter section is either a string array for aliases or a sequence of strings aliases + if (recipe[indexRecipe] instanceof String[]) { + String[] stringRecipeLines = ((String[]) recipe[indexRecipe++]); + + for (String stringRecipeLine : stringRecipeLines) { + width = stringRecipeLine.length(); + shape += stringRecipeLine; + } + + height = stringRecipeLines.length; + } else { + while (recipe[indexRecipe] instanceof String) { + String stringRecipeLine = (String) recipe[indexRecipe++]; + shape += stringRecipeLine; + width = stringRecipeLine.length(); + height++; + } + } + + // validate size + if (width * height != shape.length()) { + String stringMessage = "Invalid shaped ore recipe: "; + for (Object objectIngredient : recipe) { + stringMessage += objectIngredient + ", "; + } + stringMessage += itemStackResult; + throw new RuntimeException(stringMessage); + } + + HashMap mapInputs = new HashMap<>(); + + // third parameter section is the list of alias to component table + // convert inputs to ItemStack or ArrayList + for (; indexRecipe < recipe.length; indexRecipe += 2) { + Character character = (Character) recipe[indexRecipe]; + Object object = recipe[indexRecipe + 1]; + + if (object instanceof ItemStack) { + mapInputs.put(character, ((ItemStack) object).copy()); + } else if (object instanceof Item) { + mapInputs.put(character, new ItemStack((Item) object)); + } else if (object instanceof Block) { + mapInputs.put(character, new ItemStack((Block) object, 1, OreDictionary.WILDCARD_VALUE)); + } else if (object instanceof String) { + mapInputs.put(character, OreDictionary.getOres((String) object)); + } else { + String stringMessage = "Invalid shaped ore recipe: "; + for (Object objectIngredient : recipe) { + stringMessage += objectIngredient + ", "; + } + stringMessage += itemStackResult; + throw new RuntimeException(stringMessage); + } + } + + // save recipe inputs + itemStackIngredients = new Object[width * height]; + int indexSlot = 0; + for (char chr : shape.toCharArray()) { + itemStackIngredients[indexSlot++] = mapInputs.get(chr); + } + + // add vanilla Shaped recipe for NEI support with an impossible tag so our handler still pass through + Object[] recipeDummy = recipe.clone(); + for (int indexDummy = 0; indexDummy < recipeDummy.length; indexDummy++) { + Object objectDummy = recipeDummy[indexDummy]; + if (objectDummy instanceof ItemStack && ((ItemStack) objectDummy).hasTagCompound()) { + ItemStack itemStackDummy = ((ItemStack) objectDummy).copy(); + if (itemStackDummy.getItem() instanceof IParticleContainerItem) { + recipeDummy[indexDummy] = ((IParticleContainerItem) itemStackDummy.getItem()).getFakeVariant(itemStackDummy); + } + } + } + GameRegistry.addRecipe(new ShapedOreRecipe(result, recipeDummy)); + } + + // add ore dictionary support to an existing (vanilla) recipe + RecipeParticleShapedOre(ShapedRecipes recipe, Map replacements) { + itemStackResult = recipe.getRecipeOutput(); + width = recipe.recipeWidth; + height = recipe.recipeHeight; + + itemStackIngredients = new Object[recipe.recipeItems.length]; + + for(int i = 0; i < itemStackIngredients.length; i++) { + ItemStack itemStackIngredient = recipe.recipeItems[i]; + + if (itemStackIngredient == null) { + continue; + } + + itemStackIngredients[i] = recipe.recipeItems[i]; + + for(Entry entry : replacements.entrySet()) { + if (OreDictionary.itemMatches(entry.getKey(), itemStackIngredient, true)) { + itemStackIngredients[i] = OreDictionary.getOres(entry.getValue()); + break; + } + } + } + } + + // Returns an Item that is the result of this recipe + @Override + public ItemStack getCraftingResult(InventoryCrafting inventoryCrafting) { + return itemStackResult.copy(); + } + + // Returns the size of the recipe area + @Override + public int getRecipeSize() { + return itemStackIngredients.length; + } + + @Override + public ItemStack getRecipeOutput() { + return itemStackResult; + } + + // check if a recipe matches current crafting inventory + @Override + public boolean matches(InventoryCrafting inv, World world) { + for (int x = 0; x <= MAX_CRAFT_GRID_WIDTH - width; x++) { + for (int y = 0; y <= MAX_CRAFT_GRID_HEIGHT - height; ++y) { + if (checkMatch(inv, x, y, false)) { + return true; + } + + if (isMirrored && checkMatch(inv, x, y, true)) { + return true; + } + } + } + + return false; + } + + private boolean checkMatch(InventoryCrafting inventoryCrafting, int startX, int startY, boolean mirror) { + for (int x = 0; x < MAX_CRAFT_GRID_WIDTH; x++) { + for (int y = 0; y < MAX_CRAFT_GRID_HEIGHT; y++) { + final int subX = x - startX; + final int subY = y - startY; + Object target = null; + + if (subX >= 0 && subY >= 0 && subX < width && subY < height) { + if (mirror) { + target = itemStackIngredients[width - subX - 1 + subY * width]; + } else { + target = itemStackIngredients[subX + subY * width]; + } + } + + ItemStack itemStackSlot = inventoryCrafting.getStackInRowAndColumn(x, y); + + if (target instanceof ItemStack) {// simple ingredient + if ( itemStackSlot != null + && itemStackSlot.hasTagCompound() + && itemStackSlot.getItem() instanceof IParticleContainerItem + && ((ItemStack) target).getItem() instanceof IParticleContainerItem) { + IParticleContainerItem particleContainerItemSlot = (IParticleContainerItem) itemStackSlot.getItem(); + ParticleStack particleStackSlot = particleContainerItemSlot.getParticle(itemStackSlot); + + IParticleContainerItem particleContainerItemTarget = (IParticleContainerItem) ((ItemStack) target).getItem(); + ParticleStack particleStackTarget = particleContainerItemTarget.getParticle((ItemStack) target); + + // reject different particles or insufficient quantity + if (!particleStackSlot.containsParticle(particleStackTarget)) { + return false; + } + // mark quantity otherwise + particleContainerItemSlot.setAmountToConsume(itemStackSlot, particleStackTarget.getAmount()); + + } else if (!OreDictionary.itemMatches((ItemStack)target, itemStackSlot, false)) { + return false; + } + + } else if (target instanceof ArrayList) {// ore dictionary ingredient + boolean matched = false; + + @SuppressWarnings("unchecked") + Iterator iterator = ((ArrayList)target).iterator(); + while (iterator.hasNext() && !matched) { + matched = OreDictionary.itemMatches(iterator.next(), itemStackSlot, false); + } + + if (!matched) { + return false; + } + + } else if (target == null && itemStackSlot != null) {// ingredient found while none expected + return false; + } + } + } + + return true; + } + + public RecipeParticleShapedOre setMirrored(final boolean isMirrored) { + this.isMirrored = isMirrored; + return this; + } + + /** + * Returns the ingredients for this recipe, any mod accessing this value should never + * manipulate the values in this array as it will effect the recipe itself. + * @return The recipes itemStackIngredients vales. + */ + public Object[] getIngredients() { + return this.itemStackIngredients; + } + @Deprecated + public Object[] getInput() { + return this.itemStackIngredients; + } +} diff --git a/src/main/java/cr0s/warpdrive/config/Recipes.java b/src/main/java/cr0s/warpdrive/config/Recipes.java index b6bafff9..550e084e 100644 --- a/src/main/java/cr0s/warpdrive/config/Recipes.java +++ b/src/main/java/cr0s/warpdrive/config/Recipes.java @@ -1574,23 +1574,23 @@ public class Recipes { 'm', WarpDrive.blockElectromagnetPlain[0])); // Advanced electromagnets - GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(WarpDrive.blockElectromagnetPlain[1], 6), "mpm", "pip", "mpm", - 'i', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.ION, 500), + GameRegistry.addRecipe(new RecipeParticleShapedOre(new ItemStack(WarpDrive.blockElectromagnetPlain[1], 6), "mpm", "pip", "mpm", + 'i', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.ION, 200), 'p', ItemComponent.getItemStack(EnumComponentType.POWER_INTERFACE), 'm', WarpDrive.blockElectromagnetPlain[0])); - GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(WarpDrive.blockElectromagnetGlass[1], 6), "mpm", "pip", "mpm", - 'i', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.ION, 500), + GameRegistry.addRecipe(new RecipeParticleShapedOre(new ItemStack(WarpDrive.blockElectromagnetGlass[1], 6), "mpm", "pip", "mpm", + 'i', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.ION, 200), 'p', ItemComponent.getItemStack(EnumComponentType.POWER_INTERFACE), 'm', WarpDrive.blockElectromagnetGlass[0])); // Superior electromagnets - GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(WarpDrive.blockElectromagnetPlain[2], 6), "mtm", "sps", "mMm", + GameRegistry.addRecipe(new RecipeParticleShapedOre(new ItemStack(WarpDrive.blockElectromagnetPlain[2], 6), "mtm", "sps", "mMm", 't', ItemComponent.getItemStack(EnumComponentType.GLASS_TANK), 's', ItemComponent.getItemStack(EnumComponentType.SUPERCONDUCTOR), 'p', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.PROTON, 24), 'M', itemStackMotorHV, 'm', WarpDrive.blockElectromagnetPlain[1])); - GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(WarpDrive.blockElectromagnetGlass[2], 6), "mtm", "sps", "mMm", + GameRegistry.addRecipe(new RecipeParticleShapedOre(new ItemStack(WarpDrive.blockElectromagnetGlass[2], 6), "mtm", "sps", "mMm", 't', ItemComponent.getItemStack(EnumComponentType.GLASS_TANK), 's', ItemComponent.getItemStack(EnumComponentType.SUPERCONDUCTOR), 'p', ItemElectromagneticCell.getItemStackNoCache(ParticleRegistry.PROTON, 24), diff --git a/src/main/java/cr0s/warpdrive/item/ItemElectromagneticCell.java b/src/main/java/cr0s/warpdrive/item/ItemElectromagneticCell.java index 9e41e57c..3442433e 100644 --- a/src/main/java/cr0s/warpdrive/item/ItemElectromagneticCell.java +++ b/src/main/java/cr0s/warpdrive/item/ItemElectromagneticCell.java @@ -10,6 +10,7 @@ import cr0s.warpdrive.api.ParticleStack; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -19,6 +20,8 @@ import net.minecraft.util.StatCollector; import java.util.List; public class ItemElectromagneticCell extends Item implements IParticleContainerItem { + private static final String AMOUNT_TO_CONSUME_TAG = "amountToConsume"; + private static final int FAKE_DAMAGE = 1000; @SideOnly(Side.CLIENT) private IIcon[] icons = new IIcon[31]; @@ -29,9 +32,11 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI setCreativeTab(WarpDrive.creativeTabWarpDrive); setMaxStackSize(1); setUnlocalizedName("warpdrive.atomic.electromagnetic_cell"); + setHasSubtypes(true); } @Override + @SideOnly(Side.CLIENT) public void registerIcons(IIconRegister iconRegister) { icons[ 0] = iconRegister.registerIcon("warpdrive:atomic/electromagnetic_cell-empty"); icons[ 1] = iconRegister.registerIcon("warpdrive:atomic/electromagnetic_cell-blue-20"); @@ -67,8 +72,13 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI } @Override + @SideOnly(Side.CLIENT) public IIcon getIconFromDamage(int damage) { - return icons[damage % icons.length]; + if (damage > FAKE_DAMAGE) { + return icons[(damage - FAKE_DAMAGE) % icons.length]; + } else { + return icons[damage % icons.length]; + } } public static ItemStack getItemStackNoCache(final Particle particle, final int amount) { @@ -86,16 +96,65 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI @Override public void getSubItems(Item item, CreativeTabs creativeTab, List list) { - for(int metadata = 0; metadata < icons.length; metadata++) { - list.add(new ItemStack(item, 1, metadata)); - } - list.add(getItemStackNoCache(ParticleRegistry.ION, 100)); - list.add(getItemStackNoCache(ParticleRegistry.PROTON, 100)); - list.add(getItemStackNoCache(ParticleRegistry.ANTIMATTER, 100)); - list.add(getItemStackNoCache(ParticleRegistry.STRANGE_MATTER, 100)); + list.add(getItemStackNoCache(ParticleRegistry.ION, 1000)); + list.add(getItemStackNoCache(ParticleRegistry.PROTON, 1000)); + list.add(getItemStackNoCache(ParticleRegistry.ANTIMATTER, 1000)); + list.add(getItemStackNoCache(ParticleRegistry.STRANGE_MATTER, 1000)); // list.add(getItemStackNoCache(ParticleRegistry.TACHYONS, 100)); } + @Override + public boolean hasContainerItem(ItemStack stack) { + return true; + } + + @Override + public boolean doesContainerItemLeaveCraftingGrid(ItemStack itemStack) { + return false; + } + + @Override + public Item getContainerItem() { + return Item.getItemFromBlock(Blocks.fire); + } + + @Override + public ItemStack getContainerItem(ItemStack itemStackFilled) { + ParticleStack particleStack = getParticle(itemStackFilled); + if (particleStack != null) { + final int amount = particleStack.getAmount() - getAmountToConsume(itemStackFilled); + if (amount <= 0) { + return getItemStackNoCache(null, 0); + } + return getItemStackNoCache(particleStack.getParticle(), amount); + } + return new ItemStack(Blocks.fire); + } + + @Override + public void setAmountToConsume(ItemStack itemStack, int amountToConsume) { + ParticleStack particleStack = getParticle(itemStack); + if (particleStack == null || particleStack.getParticle() == null) { + return; + } + NBTTagCompound tagCompound = itemStack.hasTagCompound() ? itemStack.getTagCompound() : new NBTTagCompound(); + tagCompound.setInteger(AMOUNT_TO_CONSUME_TAG, amountToConsume); + } + + private int getAmountToConsume(ItemStack itemStack) { + if (itemStack.hasTagCompound()) { + return itemStack.getTagCompound().getInteger(AMOUNT_TO_CONSUME_TAG); + } + return 0; + } + + @Override + public ItemStack getFakeVariant(ItemStack itemStack) { + ItemStack itemStackFake = itemStack.copy(); + itemStackFake.setItemDamage(FAKE_DAMAGE + itemStack.getItemDamage()); + return itemStackFake; + } + private static int getDamageLevel(ItemStack itemStack, final ParticleStack particleStack) { if (!(itemStack.getItem() instanceof ItemElectromagneticCell)) { WarpDrive.logger.error("Invalid ItemStack passed, expecting ItemElectromagneticCell: " + itemStack); @@ -106,7 +165,7 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI } final ItemElectromagneticCell itemElectromagneticCell = (ItemElectromagneticCell) itemStack.getItem(); final int type = particleStack.getParticle().getColorIndex() % 5; - final double ratio = particleStack.amount / (double) itemElectromagneticCell.getCapacity(itemStack); + final double ratio = particleStack.getAmount() / (double) itemElectromagneticCell.getCapacity(itemStack); final int offset = (ratio < 0.2) ? 0 : (ratio < 0.4) ? 1 : (ratio < 0.6) ? 2 : (ratio < 0.8) ? 3 : (ratio < 1.0) ? 4 : 5; return (1 + type * 6 + offset); } @@ -137,12 +196,12 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI ParticleStack particleStack = getParticle(itemStack); if (particleStack == null || particleStack.getParticle() == null) { particleStack = new ParticleStack(resource.getParticle(), 0); - } else if (!particleStack.containsParticle(resource) || particleStack.amount >= getCapacity(itemStack)) { + } else if (!particleStack.containsParticle(resource) || particleStack.getAmount() >= getCapacity(itemStack)) { return 0; } - int consumable = Math.min(resource.amount, getCapacity(itemStack) - particleStack.amount); + int consumable = Math.min(resource.getAmount(), getCapacity(itemStack) - particleStack.getAmount()); if (!doFill) { - particleStack.amount += consumable; + particleStack.fill(consumable); NBTTagCompound tagCompound = itemStack.hasTagCompound() ? itemStack.getTagCompound() : new NBTTagCompound(); tagCompound.setTag("particle", particleStack.writeToNBT(new NBTTagCompound())); @@ -153,7 +212,7 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI @Override public ParticleStack drain(ItemStack container, int maxDrain, boolean doDrain) { - return null; + return null; // @TODO not implemented } @Override @@ -175,7 +234,7 @@ public class ItemElectromagneticCell extends Item implements IParticleContainerI final Particle particle = particleStack.getParticle(); tooltip = StatCollector.translateToLocalFormatted("item.warpdrive.atomic.electromagnetic_cell.tooltip.filled", - particleStack.amount, particle.getLocalizedName()); + particleStack.getAmount(), particle.getLocalizedName()); WarpDrive.addTooltip(list, tooltip); String particleTooltip = particle.getLocalizedTooltip();