Added recipe handler for particle container items

This commit is contained in:
LemADEC 2017-02-22 20:03:56 +01:00
parent 55519f332d
commit ccaf999e3d
5 changed files with 365 additions and 25 deletions

View file

@ -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);
// 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);
}

View file

@ -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();
}

View file

@ -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<Character, Object> mapInputs = new HashMap<>();
// third parameter section is the list of alias to component table
// convert inputs to ItemStack or ArrayList<ItemStack>
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<ItemStack, String> 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<ItemStack, String> 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<ItemStack> iterator = ((ArrayList<ItemStack>)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;
}
}

View file

@ -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),

View file

@ -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();