package com.arkcraft.module.creature.common.container.inventory; import java.util.Arrays; 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.NBTTagIntArray; import net.minecraft.nbt.NBTTagList; import net.minecraft.server.gui.IUpdatePlayerListBox; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import net.minecraft.util.MathHelper; import com.arkcraft.lib.LogHelper; import com.arkcraft.module.creature.common.entity.EntityTameableDinosaur; import com.arkcraft.module.items.ARKCraftItems; import com.arkcraft.module.items.common.general.ItemARKBase; import com.arkcraft.module.items.common.general.ItemARKFood; /** * @author wildbill22 */ public class InventoryTaming implements IInventory, IUpdatePlayerListBox { /** * The dino whose inventory this is. */ EntityTameableDinosaur entityDino; /** * The player who started the taming process (with the GUI). */ public EntityPlayer playerTaming; // Other class variables int tick = 20; public InventoryTaming(EntityTameableDinosaur entityDino) { this.entityDino = entityDino; TAMING_TIME_FOR_COMPLETION = (short) entityDino.getTamingSeconds(); } // Create and initialize the itemStacks variable that will store store the // itemStacks public static final int NARCO_SLOTS_COUNT = 1; public static final int FOOD_SLOTS_COUNT = 1; public static final int TOTAL_SLOTS_COUNT = NARCO_SLOTS_COUNT + FOOD_SLOTS_COUNT; public static final int NARCO_SLOT = 0; public static final int FOOD_SLOT = 1; public static final int FIRST_INPUT_SLOT = NARCO_SLOT; private ItemStack[] itemStacks = new ItemStack[TOTAL_SLOTS_COUNT]; /** * Set true whenever the inventory changes. Nothing sets it false so you * will have to write your own code to check it and reset the value. */ public boolean inventoryChanged; /** * The number of feeding ticks remaining on the current piece of food */ private int[] feedingTimeRemaining = new int[FOOD_SLOTS_COUNT]; /** * The initial food value of the currently feeding food (in ticks of feeding * duration) */ private int[] feedingTimeInitialValue = new int[FOOD_SLOTS_COUNT]; /** * The number of ticks the dino has been taming */ private short tamingTime; /** * The number of ticks required to tame the dino */ private short TAMING_TIME_FOR_COMPLETION = 50; // vanilla value is 50 // seconds /** * Number of ticks dino will remain unconscious */ private short torporTime = 0; /** * Torpor time maximum that can be set */ private static short MAX_TORPOR_TIME = 150; @SuppressWarnings("unused") private int cachedNumberOfFeedingSlots = -1; /** * Returns the amount of food remaining on the currently feeding item in the * given food slot. * * @param foodSlot * the number of the food slot (1) * @return fraction remaining, between 0 - 1 */ public double fractionOfFoodRemaining(int foodSlot) { if (feedingTimeInitialValue[foodSlot] <= 0) { return 0; } double fraction = feedingTimeRemaining[foodSlot] / (double) feedingTimeInitialValue[foodSlot]; return MathHelper.clamp_double(fraction, 0.0, 1.0); } /** * return the remaining feed time of the food in the given slot * * @param foodSlot * the number of the food slot (1) * @return seconds remaining */ public int secondsOfFoodRemaining(int foodSlot) { if (feedingTimeRemaining[foodSlot] <= 0) { return 0; } return feedingTimeRemaining[foodSlot]; } /** * Get the number of slots which have food in them. * * @return number of slots with food, 0 - FOOD_SLOTS_COUNT */ public int numberOfFoodInSlots() { int foodCount = 0; for (int feedingTime : feedingTimeRemaining) { if (feedingTime > 0) { ++foodCount; } } return foodCount; } /** * Returns the amount of taming time completed. * * @return fraction remaining, between 0 - 1 */ public double fractionOfTamingTimeComplete() { double fraction = tamingTime / (double) TAMING_TIME_FOR_COMPLETION; return MathHelper.clamp_double(fraction, 0.0, 1.0); } // This method is called every tick to update the entity, i.e. // - see if the food runs out, and if so turn the taming "off" and slowly // untames the dino // - use any tranquilizer to increase the torpor level // - see if the tranquilizer runs out, and "wake" the dino if it does // - see if dino is tamed // It runs both on the server and the client. @Override public void update() { if (tick >= 0) { tick--; return; } else { tick = 20; } // If there is nothing to feed or not unconscious, reset feedTime and // return if (canTame()) { int numberOfFoodFeeding = feedDino(); tranqDino(); // If food is available, keep taming, otherwise start "untaming" it // at double speed if (numberOfFoodFeeding > 0) { tamingTime += numberOfFoodFeeding; } else { tamingTime -= 2; } if (tamingTime < 0) { tamingTime = 0; } torporTime--; // Failed to tame? if (torporTime <= 0) { tamingTime = 0; torporTime = 0; this.entityDino.setSitting(false); this.setTorporTime((short) 0); if (this.entityDino.worldObj != null & this.entityDino.worldObj.isRemote) { LogHelper.info("InventoryTaming - update: setSitting(false) from client"); } else { LogHelper.info("InventoryTaming - update: setSitting(false) from server"); } } // If tamingTime has reached maxTamingTime the dino is tamed if (tamingTime >= TAMING_TIME_FOR_COMPLETION) { setToTamed(); tamingTime = 0; torporTime = 0; } } else { tamingTime = 0; } } /** * For each food slot: decreases the feed time, checks if feedTimeRemaining * = 0 and tries to consume a new piece of food if one is available * * @return the number of food slots which are being eaten */ private int feedDino() { int feedingCount = 0; boolean inventoryChanged = false; // Iterate over all the food slots for (int i = 0; i < FOOD_SLOTS_COUNT; i++) { int foodSlotNumber = i + FOOD_SLOT; if (feedingTimeRemaining[i] > 0) { --feedingTimeRemaining[i]; ++feedingCount; } if (feedingTimeRemaining[i] == 0) { if (itemStacks[foodSlotNumber] != null && getItemFeedTime(itemStacks[foodSlotNumber]) > 0) { // If the stack in this slot is not null and is fuel, set // feedingTimeRemaining & feedingTimeInitialValue to the // item's feed time and decrease the stack size feedingTimeRemaining[i] = feedingTimeInitialValue[i] = getItemFeedTime(itemStacks[foodSlotNumber]); --itemStacks[foodSlotNumber].stackSize; ++feedingCount; inventoryChanged = true; // If the stack size now equals 0 set the slot contents to // the items container item. This is for fuel // items such as lava buckets so that the bucket is not // consumed. If the item dose not have // a container item getContainerItem returns null which sets // the slot contents to null if (itemStacks[foodSlotNumber].stackSize == 0) { itemStacks[foodSlotNumber] = itemStacks[foodSlotNumber].getItem() .getContainerItem(itemStacks[foodSlotNumber]); } } } } if (inventoryChanged) { markDirty(); } return feedingCount; } /** * Returns the seconds of feeding time left. * * @return seconds */ public int secondsOfFeedingTimeLeft() { if (itemStacks[FOOD_SLOT] != null) { return itemStacks[FOOD_SLOT].stackSize * feedingTimeInitialValue[0] + feedingTimeRemaining[0]; } else { return feedingTimeRemaining[0]; } } // Tranquilize dino with a berry private void tranqDino() { boolean inventoryChanged = false; if (itemStacks[NARCO_SLOT] != null) { torporTime += getTranqTime(itemStacks[NARCO_SLOT]); if (torporTime > MAX_TORPOR_TIME) { torporTime = MAX_TORPOR_TIME; } --itemStacks[NARCO_SLOT].stackSize; inventoryChanged = true; // If the stack size now equals 0 set the slot contents to the items // container item. This is for fuel // items such as lava buckets so that the bucket is not consumed. If // the item dose not have // a container item getContainerItem returns null which sets the // slot contents to null if (itemStacks[NARCO_SLOT].stackSize == 0) { itemStacks[NARCO_SLOT] = itemStacks[NARCO_SLOT].getItem().getContainerItem( itemStacks[NARCO_SLOT]); } } if (inventoryChanged) { markDirty(); } } /** * Check if the dino is tameable * * @return true if taming is complete */ private boolean canTame() { return this.entityDino.isTameable() && !this.entityDino.isTamed() && torporTime != 0; } /** * Tame the dino, if possible * * @return */ private void setToTamed() { this.entityDino.setTamed(playerTaming, true); this.setTorporTime((short) 0); this.entityDino.setIsTaming(false); // Copy the taming inventory to the new tamed inventory for (int i = 0; i < entityDino.invTaming.getSizeInventory() && i < entityDino.invTamedDino .getSizeInventory(); i++) { entityDino.invTamedDino.setInventorySlotContents(i, entityDino.invTaming.getStackInSlot(i)); } } // returns the number of ticks the the food will feed. Returns 0 if the item // will not feed the dino public static short getItemFeedTime(ItemStack stack) { int feedtime = ItemARKFood.getItemFeedTime(stack); return (short) MathHelper.clamp_int(feedtime, 0, Short.MAX_VALUE); } // returns the number of ticks the food will tranquilize. Returns 0 if the // item does not tranquilize public static short getTranqTime(ItemStack stack) { if (stack.getItem() instanceof ItemARKBase) { return (short) MathHelper.clamp_int( ItemARKBase.getItemTorporTime(stack), 0, Short.MAX_VALUE); } if (stack.getItem() instanceof ItemARKFood) { return (short) MathHelper.clamp_int( ItemARKFood.getItemTorporTime(stack), 0, Short.MAX_VALUE); } return 0; } @Override public String getName() { return entityDino.getName(); } @Override public boolean hasCustomName() { return false; } @Override public IChatComponent getDisplayName() { return this.hasCustomName() ? new ChatComponentText(this.getName()) : new ChatComponentTranslation( this.getName()); } @Override public int getSizeInventory() { return itemStacks.length; } @Override public ItemStack getStackInSlot(int index) { return itemStacks[index]; } @Override public ItemStack decrStackSize(int slotIndex, int count) { ItemStack itemStackInSlot = getStackInSlot(slotIndex); if (itemStackInSlot == null) { return null; } ItemStack itemStackRemoved; if (itemStackInSlot.stackSize <= count) { itemStackRemoved = itemStackInSlot; setInventorySlotContents(slotIndex, null); } else { itemStackRemoved = itemStackInSlot.splitStack(count); if (itemStackInSlot.stackSize == 0) { setInventorySlotContents(slotIndex, null); } } markDirty(); return itemStackRemoved; } @Override public ItemStack getStackInSlotOnClosing(int index) { return null; } @Override public void setInventorySlotContents(int slotIndex, ItemStack itemstack) { itemStacks[slotIndex] = itemstack; if (itemstack != null && itemstack.stackSize > getInventoryStackLimit()) { itemstack.stackSize = getInventoryStackLimit(); } markDirty(); } @Override public int getInventoryStackLimit() { return 64; } @Override public boolean isUseableByPlayer(EntityPlayer player) { return entityDino.isUseableByPlayer(player); } @Override public void openInventory(EntityPlayer player) { // TODO Auto-generated method stub } @Override public void closeInventory(EntityPlayer player) { // TODO Auto-generated method stub } @Override public boolean isItemValidForSlot(int index, ItemStack stack) { // TODO Auto-generated method stub return false; } @Override public void clear() { Arrays.fill(itemStacks, null); } // Returns double between 0 and 1 representing % full level public double unconciousLevel() { double fraction = torporTime / (double) MAX_TORPOR_TIME; return MathHelper.clamp_double(fraction, 0.0, 1.0); } // Returns double between 0 and 1 representing % full level public double tamingLevel() { double fraction = tamingTime / (double) TAMING_TIME_FOR_COMPLETION; return MathHelper.clamp_double(fraction, 0.0, 1.0); } // Return true if stack will tranquilize the dino public boolean isItemValidForNarcoSlot(ItemStack stack) { if (stack != null && stack.getItem() == ARKCraftItems.narcoBerry || stack.getItem() == ARKCraftItems.narcotics) { return true; } else { return false; } } // Return true if stack is a valid seed for the crop plot public boolean isItemValidForFoodSlot(ItemStack stack) { if (ItemARKFood.getItemFeedTime(stack) > 0) { return true; } else { return false; } } // ------------------------------ // Fields are used to send non-inventory information from the server to // interested clients // The container code caches the fields and sends the client any fields // which have changed. // The field ID is limited to byte, and the field value is limited to short. // (if you use more than this, they get cast down // in the network packets) // If you need more than this, or shorts are too small, use a custom packet // in your container instead. private static final byte UNCONSCIOUS_FIELD_ID = 0; private static final byte TAMING_FIELD_ID = 1; private static final byte NUMBER_OF_FIELDS = 2; @Override public int getField(int id) { if (id == UNCONSCIOUS_FIELD_ID) { return torporTime; } if (id == TAMING_FIELD_ID) { return tamingTime; } System.err.println("Invalid field ID in InventoryTaming.getField:" + id); return 0; } @Override public void setField(int id, int value) { if (id == UNCONSCIOUS_FIELD_ID) { torporTime = (short) value; } else if (id == TAMING_FIELD_ID) { tamingTime = (short) value; } else { System.err.println("Invalid field ID in InventoryTaming.setField:" + id); } } @Override public int getFieldCount() { return NUMBER_OF_FIELDS; } @Override public void markDirty() { this.inventoryChanged = true; } // ------------------------------ // This is where you save any data that you don't want to lose when the tile // entity unloads // In this case, it saves the state of the furnace (burn time etc) and the // itemstacks stored in the fuel, input, and output slots public void saveInventoryToNBT(NBTTagCompound parentNBTTagCompound) { // to use an analogy with Java, this code generates an array of hashmaps // The itemStack in each slot is converted to an NBTTagCompound, which // is effectively a hashmap of key->value pairs such // as slot=1, id=2353, count=1, etc // Each of these NBTTagCompound are then inserted into NBTTagList, which // is similar to an array. NBTTagList dataForAllSlots = new NBTTagList(); for (int i = 0; i < this.itemStacks.length; ++i) { if (this.itemStacks[i] != null) { NBTTagCompound dataForThisSlot = new NBTTagCompound(); dataForThisSlot.setByte("Slot", (byte) i); this.itemStacks[i].writeToNBT(dataForThisSlot); dataForAllSlots.appendTag(dataForThisSlot); } } // the array of hashmaps is then inserted into the parent hashmap for // the container parentNBTTagCompound.setTag("Items", dataForAllSlots); // Save everything else parentNBTTagCompound.setTag("burnTimeRemaining", new NBTTagIntArray(feedingTimeRemaining)); parentNBTTagCompound.setTag("burnTimeInitial", new NBTTagIntArray(feedingTimeInitialValue)); parentNBTTagCompound.setShort("tamingTime", tamingTime); parentNBTTagCompound.setShort("torporTime", torporTime); } // This is where you load the data that you saved in writeToNBT public void loadInventoryFromNBT(NBTTagCompound nbtTagCompound) { final byte NBT_TYPE_COMPOUND = 10; // See NBTBase.createNewByType() for // a listing NBTTagList dataForAllSlots = nbtTagCompound.getTagList("Items", NBT_TYPE_COMPOUND); Arrays.fill(itemStacks, null); // set all slots to empty for (int i = 0; i < dataForAllSlots.tagCount(); ++i) { NBTTagCompound dataForOneSlot = dataForAllSlots.getCompoundTagAt(i); byte slotNumber = dataForOneSlot.getByte("Slot"); if (slotNumber >= 0 && slotNumber < this.itemStacks.length) { this.itemStacks[slotNumber] = ItemStack.loadItemStackFromNBT(dataForOneSlot); } } // Load everything else. Trim the arrays (or pad with 0) to make sure // they have the correct number of elements feedingTimeRemaining = Arrays.copyOf(nbtTagCompound.getIntArray("feedingTimeRemaining"), FOOD_SLOTS_COUNT); feedingTimeInitialValue = Arrays.copyOf( nbtTagCompound.getIntArray("feedingTimeInitialValue"), FOOD_SLOTS_COUNT); tamingTime = nbtTagCompound.getShort("tamingTime"); torporTime = nbtTagCompound.getShort("torporTime"); cachedNumberOfFeedingSlots = -1; } /** * Returns true when at max torpor */ public boolean setTorporTime(short i) { torporTime = i; if (torporTime > MAX_TORPOR_TIME) { torporTime = MAX_TORPOR_TIME; return true; } return false; } public int getTorporTime() { return torporTime; } // When the world loads from disk, the server needs to send the TileEntity // information to the client // it uses getDescriptionPacket() and onDataPacket() to do this // @Override // public Packet getDescriptionPacket() { // NBTTagCompound nbtTagCompound = new NBTTagCompound(); // writeToNBT(nbtTagCompound); // final int METADATA = 0; // return new S35PacketUpdateTileEntity(this.pos, METADATA, nbtTagCompound); // } // // public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity // pkt) { // loadInventoryFromNBT(pkt.getNbtCompound()); // } // ------------------------ }