Posted January 20, 20178 yr Good evening, I am trying to set up a GUI to equip my custom Entity, meaning giving it some armor pieces or items in hands. Here is how it looks like right now : ContainerNPCInventory: public class ContainerNPCInventory extends Container { private static final EntityEquipmentSlot[] VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] { EntityEquipmentSlot.HEAD, EntityEquipmentSlot.CHEST, EntityEquipmentSlot.LEGS, EntityEquipmentSlot.FEET }; private final EntityNPCDialog entityNPC; private Iterable<ItemStack> inventoryArmor; private Iterable<ItemStack> heldEquipment; private final InventoryPlayer playerInventory; private InventoryBasic entityInventory; public ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) { this.entityNPC = entityNPC; this.inventoryArmor = entityNPC.getArmorInventoryList(); this.heldEquipment = entityNPC.getHeldEquipment(); this.playerInventory = playerIn.inventory; this.entityInventory = new InventoryBasic("NPC Inventory", true, Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment)); this.entityInventory.addInventoryChangeListener(entityNPC); for (int index = 0; index < 4; ++index) { final EntityEquipmentSlot equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index]; this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) { @Override public boolean isItemValid(@Nullable ItemStack stack) { return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC)); } @Override public int getSlotStackLimit() { return (1); } @Nullable @Override public String getSlotTexture() { return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]); } }); } this.addSlotToContainer(new Slot(this.entityInventory, 4, 77, ); this.addSlotToContainer(new Slot(this.entityInventory, 5, 95, { @Nullable @Override public String getSlotTexture() { return ("minecraft:items/empty_armor_slot_shield"); } }); for (int index = 0; index < 9; ++index) { this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112)); } } @Override public boolean canInteractWith(EntityPlayer playerIn) { return (true); } } EntityNPCDialog: public class EntityNPCDialog extends EntityLiving implements IInventoryChangedListener { // private static final DataParameter<Integer> TEXTURE_INDEX = EntityDataManager.<Integer>createKey(EntityNPCDialog.class, DataSerializers.VARINT); public EntityNPCDialog(World worldIn) { super(worldIn); } @Override protected void initEntityAI() { super.initEntityAI(); this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 16.0F, 42.0F)); this.tasks.addTask(2, new EntityAILookIdle(this)); } @Override public boolean canBePushed() { return (false); } @Override public boolean isPushedByWater() { return (false); } @Override protected boolean canDespawn() { return(false); } @Override public void addVelocity(double x, double y, double z) {} @Override public boolean canRiderInteract() { return (false); } @Override public boolean isEntityInvulnerable(DamageSource source) { return (false); } @Override public boolean isPotionApplicable(PotionEffect potioneffectIn) { return (false); } @Override public boolean isImmuneToExplosions() { return (false); } @Nullable @Override protected SoundEvent getAmbientSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Nullable @Override protected SoundEvent getHurtSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Nullable @Override protected SoundEvent getDeathSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getFallSound(int heightIn) { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getSwimSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getSplashSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } public static ThreadDownloadImageData getDownloadImageSkin(ResourceLocation resourceLocationIn) { TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); ThreadDownloadImageData threadDownloadImageData; threadDownloadImageData = new ThreadDownloadImageData(null, "http://imgur.com/Z0pbA3P.png", DefaultNPCSkin.getDefaultSkin(), new ImageBufferDownload() { @Override public BufferedImage parseUserSkin(BufferedImage image) { return (image); } }); textureManager.loadTexture(resourceLocationIn, threadDownloadImageData); return (threadDownloadImageData); } private void updateNPCSlots(InventoryBasic inventoryBasic) { if (!this.worldObj.isRemote) { this.setItemStackToSlot(EntityEquipmentSlot.HEAD, inventoryBasic.getStackInSlot(0)); this.setItemStackToSlot(EntityEquipmentSlot.CHEST, inventoryBasic.getStackInSlot(1)); this.setItemStackToSlot(EntityEquipmentSlot.LEGS, inventoryBasic.getStackInSlot(2)); this.setItemStackToSlot(EntityEquipmentSlot.FEET, inventoryBasic.getStackInSlot(3)); this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, inventoryBasic.getStackInSlot(4)); this.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, inventoryBasic.getStackInSlot(5)); } } @Override public void onInventoryChanged(InventoryBasic inventoryBasic) { this.updateNPCSlots(inventoryBasic); } } I must admit I am really confused. I got inspired by the GuiContainerCreative, ContainerHorseInventory (the container for horses) and EntityZombie, but it seems that I am going in the wrong way. What I would like to achieve is that, when I place an item in the Slots of my container, it updates the equipment and hands ItemStack arrays of my Entity (+ rendering the items on the entity, like Zombies or Skeletons but this should work since my Entity model extends from ModelBiped). I think I am complicating the task for no reason. Could you guys highlight me on what should I achieve this ? Thank you for your time. Squirrel ! Squirrel ! Squirrel !
January 21, 20178 yr Author Good evening, After checking my code again, I don't really understand why it does not work at all. The EntityLivingBase#setItemStackToSlot(EntityEquipmentSlot slotIn, @Nullable ItemStack stack) method should work, but in my case I think I'm not using it in the right way. Basically, I've made some simple tests to check if my entity could currently hold those items. One of this test is very simplistic: when I proceed a right-click on my entity with an equipment item, I call the method above from the server. NPCInteractEvent: @SubscribeEvent public void onEntityNPCInteract(PlayerInteractEvent.EntityInteract event) { ItemStack heldItem = event.getEntityPlayer().getHeldItem(EnumHand.MAIN_HAND); ItemBase npcManager = CraftAndConquerItems.npcManager; if (event.getTarget() instanceof EntityNPCDialog && event.getHand().equals(EnumHand.MAIN_HAND) && heldItem != null) { if (!event.getWorld().isRemote) { if (heldItem.getItem().equals(npcManager)) event.getEntityPlayer().openGui(CraftAndConquer.instance, GuiHandler.CAC_NPC_GUI, event.getWorld(), event.getTarget().getEntityId(), 0, 0); else if (heldItem.getItem().equals(Items.DIAMOND_HELMET)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.HEAD, heldItem); else if (heldItem.getItem().equals(Items.DIAMOND_CHESTPLATE)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.CHEST, heldItem); else if (heldItem.getItem().equals(Items.DIAMOND_LEGGINGS)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.LEGS, heldItem); else if (heldItem.getItem().equals(Items.DIAMOND_BOOTS)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.FEET, heldItem); else if (heldItem.getItem().equals(Items.DIAMOND_SWORD)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.MAINHAND, heldItem); else if (heldItem.getItem().equals(Items.SHIELD)) event.getTarget().setItemStackToSlot(EntityEquipmentSlot.OFFHAND, heldItem); } } } The event does actually work, even when I disconnect/reconnect, the equipment has been successfully saved in NBTTags, here is the result: https://images.discordapp.net/.eJwFwVEOgyAMANC7cABqO4vM2xAkaNSWQM0-lt19733d0y-3ut2sjRVgO0bWvvlh2lMtvqrWq6R2DJ_1hmSW8n4XsQEUkAnDRMSMFJjeQAu9GJc4R8LIEyLBI6foR3yT6n5_BBkixQ.vHGe_Qq0t2Fo_YJzemzOELu6gjU?width=1250&height=677[/img] But still, through a GUI, my code does not work. What is strange is that, when those entities are wearing the equipment in the event andler way, it does not appear in the GUI: I don't really want to add an Inventory in my Entity class, since I only want to access to the existing inventoryHands and inventoryArmor fields of the EntityLiving class. I don't see the point to add an Inventory that includes what already exists in their own way. I feel that this is something stupid that I am missing, but I don't see what. Squirrel ! Squirrel ! Squirrel !
January 21, 20178 yr What do you think this does, this.entityInventory = new InventoryBasic("NPC Inventory", true, Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment)); VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
January 21, 20178 yr Author What do you think this does, this.entityInventory = new InventoryBasic("NPC Inventory", true, Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment)); It does create an empty InventoryBasic, which I use it to make the link between the already-existing inventoryHands and inventoryArmor fields from LivingEntity and my Container, but it does not seem to work at all. I use them because Slots don't support Iterable<ItemStack> to be constructed. Squirrel ! Squirrel ! Squirrel !
January 21, 20178 yr Create a custom Inventory that does then. VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
January 21, 20178 yr Author Create a custom Inventory that does then. What do you mean by creating a "custom Inventory" ? Is the InventoryBasic not sufficient for this kind of things ? Also, I am surprised that nobody tried to develop this in Forge. I searched few minutes on google and different forge forums and I have not been able to find a single topic talking about this. Squirrel ! Squirrel ! Squirrel !
January 21, 20178 yr Create a custom Inventory that does then. What do you mean by creating a "custom Inventory" ? Is the InventoryBasic not sufficient for this kind of things ? Also, I am surprised that nobody tried to develop this in Forge. I searched few minutes on google and different forge forums and I have not been able to find a single topic talking about this. Create a class that extends InventoryBasic. Then in the constructor have it pass ItemStack Arrays and loop through them and add them to the already existing array with setInventorySlotContents or addItem. VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
January 22, 20178 yr Author Good evening, So I tried the suggestion of Animefan8888: Create a class that extends InventoryBasic. Then in the constructor have it pass ItemStack Arrays and loop through them and add them to the already existing array with setInventorySlotContents or addItem. Here is the code. NPCInventoryBasic: public class NPCInventoryBasic extends InventoryBasic { public NPCInventoryBasic(Iterable<ItemStack> inventoryArmor, Iterable<ItemStack> inventoryHands) { super("NPC Inventory Basic", true, Iterables.size(inventoryArmor) + Iterables.size(inventoryHands)); Iterable<ItemStack> equipment = Iterables.concat(inventoryArmor, inventoryHands); for (int index = 0; index < Iterables.size(inventoryArmor) + Iterables.size(inventoryHands); ++index) { this.setInventorySlotContents(index, Iterables.get(equipment, index)); } } } ContainerNPCInventory: public class ContainerNPCInventory extends Container { private static final EntityEquipmentSlot[] VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] { EntityEquipmentSlot.HEAD, EntityEquipmentSlot.CHEST, EntityEquipmentSlot.LEGS, EntityEquipmentSlot.FEET }; private EntityNPCDialog entityNPC; private final InventoryPlayer playerInventory; private NPCInventoryBasic entityInventory; public ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) { Iterable<ItemStack> inventoryArmor; Iterable<ItemStack> heldEquipment; this.entityNPC = entityNPC; inventoryArmor = entityNPC.getArmorInventoryList(); heldEquipment = entityNPC.getHeldEquipment(); if (!playerIn.worldObj.isRemote) { inventoryArmor.forEach(armor -> CraftAndConquer.logger.log(Level.INFO, "[ARMOR] I am:\t" + (armor != null ? armor.getDisplayName() : "null"))); heldEquipment.forEach(hand -> CraftAndConquer.logger.log(Level.INFO, "[HANDS] I am:\t" + (hand != null ? hand.getDisplayName() : "null"))); } this.playerInventory = playerIn.inventory; this.entityInventory = new NPCInventoryBasic(inventoryArmor, heldEquipment); //this.entityInventory.addInventoryChangeListener(this.entityNPC); for (int index = 0; index < 4; ++index) { final EntityEquipmentSlot equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index]; this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(equipmentSlot, this.getStack()); super.onSlotChanged(); } @Override public boolean isItemValid(@Nullable ItemStack stack) { return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC)); } @Override public int getSlotStackLimit() { return (1); } @Nullable @Override public String getSlotTexture() { return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]); } }); } this.addSlotToContainer(new Slot(this.entityInventory, 4, 77, { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, this.getStack()); super.onSlotChanged(); } }); this.addSlotToContainer(new Slot(this.entityInventory, 5, 95, { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, this.getStack()); super.onSlotChanged(); } @Nullable @Override public String getSlotTexture() { return ("minecraft:items/empty_armor_slot_shield"); } }); for (int index = 0; index < 9; ++index) { this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112)); } } @Override public boolean canInteractWith(EntityPlayer playerIn) { return (true); } } I think it works. Well, it "half" works. Wtf haha. When I place the equipment in the different slots, it updates the entity correctly as it equips and renders all the stuff nicely. But when I leave the GUI and open it again, this kind of thing happens as above (meaning, all the armor pieces are reversed, but not the hands). I just don't understand why, the indexes of the EntityEquipmentSlot enums are correct, the slots in my container didn't change, so what could happen ?? Squirrel ! Squirrel ! Squirrel !
January 22, 20178 yr Minecraft started it's IDs for armor at zero = to boots, and so on upwards. I know a little strange right. VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
January 22, 20178 yr Author Minecraft started it's IDs for armor at zero = to boots, and so on upwards. I know a little strange right. I figured it out by myself and your comment confirmed what I noticed, thanks ! I managed to make it work without a custom inventory and by using EntityLiving#getItemStackFromSlot(EntityEquipmentSlot slotIn) and EntityLiving#setItemStackToSlot(EntityEquipmentSlot slotIn, @Nullable ItemStack stack) instead of going through the Iterable<ItemStack>. EntityNPCDialog: public class EntityNPCDialog extends EntityLiving { // private static final DataParameter<Integer> TEXTURE_INDEX = EntityDataManager.<Integer>createKey(EntityNPCDialog.class, DataSerializers.VARINT); public EntityNPCDialog(World worldIn) { super(worldIn); } @Override protected void initEntityAI() { super.initEntityAI(); this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 16.0F, 42.0F)); this.tasks.addTask(2, new EntityAILookIdle(this)); } @Override public boolean canBePushed() { return (false); } @Override public boolean isPushedByWater() { return (false); } @Override protected boolean canDespawn() { return(false); } @Override public void addVelocity(double x, double y, double z) {} @Override public boolean canRiderInteract() { return (false); } @Override public boolean isEntityInvulnerable(DamageSource source) { return (false); } @Override public boolean isPotionApplicable(PotionEffect potioneffectIn) { return (false); } @Override public boolean isImmuneToExplosions() { return (false); } @Nullable @Override protected SoundEvent getAmbientSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Nullable @Override protected SoundEvent getHurtSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Nullable @Override protected SoundEvent getDeathSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getFallSound(int heightIn) { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getSwimSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } @Override protected SoundEvent getSplashSound() { return (CraftAndConquerSounds.NPCDialogSoundEvent); } // public static ThreadDownloadImageData getDownloadImageSkin(ResourceLocation resourceLocationIn) { // TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); // ThreadDownloadImageData threadDownloadImageData; // // threadDownloadImageData = new ThreadDownloadImageData(null, "http://imgur.com/Z0pbA3P.png", DefaultNPCSkin.getDefaultSkin(), new ImageBufferDownload() { // // @Override // public BufferedImage parseUserSkin(BufferedImage image) { // return (image); // } // // }); // // textureManager.loadTexture(resourceLocationIn, threadDownloadImageData); // // return (threadDownloadImageData); // } } ContainerNPCInventory: public class ContainerNPCInventory extends Container { public static final EntityEquipmentSlot[] VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] { EntityEquipmentSlot.HEAD, EntityEquipmentSlot.CHEST, EntityEquipmentSlot.LEGS, EntityEquipmentSlot.FEET, EntityEquipmentSlot.MAINHAND, EntityEquipmentSlot.OFFHAND }; private EntityNPCDialog entityNPC; private final InventoryPlayer playerInventory; private InventoryBasic entityInventory; public ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) { this.entityNPC = entityNPC; this.playerInventory = playerIn.inventory; this.entityInventory = new InventoryBasic("NPC Inventory", true, ContainerNPCInventory.VALID_EQUIPMENT_SLOTS.length); for (int index = 0; index < ContainerNPCInventory.VALID_EQUIPMENT_SLOTS.length; ++index) { this.entityInventory.setInventorySlotContents(index, entityNPC.getItemStackFromSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index])); } for (int index = 0; index < 4; ++index) { final EntityEquipmentSlot equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index]; this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(equipmentSlot, this.getStack()); super.onSlotChanged(); } @Override public boolean isItemValid(@Nullable ItemStack stack) { return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC)); } @Override public int getSlotStackLimit() { return (1); } @Nullable @Override public String getSlotTexture() { return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]); } }); } this.addSlotToContainer(new Slot(this.entityInventory, 4, 77, { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[4], this.getStack()); super.onSlotChanged(); } }); this.addSlotToContainer(new Slot(this.entityInventory, 5, 95, { @Override public void onSlotChanged() { entityNPC.setItemStackToSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[5], this.getStack()); super.onSlotChanged(); } @SideOnly(Side.CLIENT) @Nullable @Override public String getSlotTexture() { return ("minecraft:items/empty_armor_slot_shield"); } }); for (int index = 0; index < 9; ++index) { this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112)); } } @Override public boolean canInteractWith(EntityPlayer playerIn) { return (true); } } It seems to work on multiplayer, I tested and I didn't have any crash. Also, the server stops correctly and restarts correctly with the correct equipement on the Entity. One last question though: when putting a debug message in Slot#onSlotChanged() method, I noticed that this method was called multiple times, either client side or server side. Why is that ? And does it influence the Entity stats when I put the equipment once ? (like summing the armor stats because the method is called multiple times) Squirrel ! Squirrel ! Squirrel !
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.