Équiper une Entité custom (armure + objets dans les mains) aux travers d'une GUI



  • Hello !

    Je fais suivre mon topic sur le forum anglophone, celui-ci semble ne pas soulever d'idées pour le moment. 😄

    J'ai créé une GUI qui me permet de changer l'équipement de mon entité custom (un NPC), mais visiblement ça ne fonctionne pas très bien (du tout).

    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, 8));
    
           this.addSlotToContainer(new Slot(this.entityInventory, 5, 95, 8) {
    
               @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);
       }
    }
    
    

    Pour faire tout cela, je me suis inspiré de GuiContainerCreative qui est la GUI du joueur quand il est en creative mode, de ContainerHorseInventory qui est le container pour les chevaux et d'EntityZombie. Ce que je souhaite faire aux travers de mon code, c'est dès que je place un équipement dans le slot associé de la GUI, ça équipe mon NPC de cet équipement en question. Et évidemment, dès que je le retire, ça enlève l'équipement en question.

    Les ItemStack[] des équipements sont inventoryHands et inventoryArmor dans EntityLiving. Évidemment, on ne peut pas y accéder directement puisque ces variables sont private. Pour cela, il y a la méthode EntityLiving#setItemStackToSlot(EntityEquipmentSlot slotIn, @Nullable ItemStack stack) qui met l'item au bon endroit (soit dans les mains, soit dans l'armure). Mais même en appelant cette méthode dans mon code ça ne fonctionne pas.

    Je ne sais pas trop quoi faire, j'ai essayé bien des choses comme override la méthode Slot#onSlotChanged(), faire que mon Entity implémente IInventoryChangedListener, mais je n'ai toujours pas le résultat attendu. Les items rentrent bien dans les slots, certes, mais dès que je quitte la GUI et que je reviens dessus, les items disparaissent (je n'ai pas besoin de write ou read les NBTTags, EntityLiving le fait déjà à ma place). Quand je place l'item dans le slot, il ne se passe rien, l'entité n'équipe même pas l'équipement en question (test visible puisque si je mets un casque sur l'entité, on le voit sur sa tête, hors là ça ne marche pas). J'ai essayé une approche beaucoup plus simpliste qui est que, quand je clique droit sur mon entité avec un objet, il l'équipe. Et devinez quoi, ça marche.

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

    Le code ci-dessus fonctionne. Les items sont bien sauvegardés dans l'entité, et quand je déco-reco ils restent bien là, mais impossible de faire ça en passant par une GUI.

    Avez-vous une petite idée ?  :'(</integer></integer></itemstack></itemstack>



  • Bizarre…mets deux prints dans la fonction updateNPCSlots, un avant et un après le if(), et regarde si ça serait pas la fonction qui est appelé que côté client, si oui, montres comment tu ouvres ton gui.



  • @'AymericRed':

    Bizarre…mets deux prints dans la fonction updateNPCSlots, un avant et un après le if(), et regarde si ça serait pas la fonction qui est appelé que côté client, si oui, montres comment tu ouvres ton gui.

    Alors, j'ai mis un message log avant, pendant, et après. Voici les logs au moment où la GUI s'ouvre :

    
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:55:14] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    
    

    Et voici les logs au moment où je mets l'item dans un slot:

    
    [17:53:40] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:53:40] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:53:40] [Client thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Client thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [DURING] This message is called DURING the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [DURING] This message is called DURING the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [BEFORE] This message is called BEFORE the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [DURING] This message is called DURING the if condition !
    [17:53:40] [Server thread/INFO] [craftandconquer]: [AFTER] This message is called AFTER the if condition !
    
    

    Je ne sais pas trop comment interpréter le résultat.



  • Bonsoir !

    J'ai résolu mon problème. 🙂 Et je ne sais pas trop comment …

    Après avoir fait plusieurs tests, il s'avère que je n'utilise plus d'Iterable<itemstack> mais j'appelle directement les méthodes EntityLiving#getItemStackFromSlot et EntityLiving#setItemStackFromSlot pour mettre à jour correctement l'InventoryBasic et mon Entity (pour faire la passerelle correctement entre les deux quoi).

    Voici le code qui fonctionne, si jamais quelqu'un passe après moi.

    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, 8) {
    
               @Override
               public void onSlotChanged() {
                   entityNPC.setItemStackToSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[4], this.getStack());
                   super.onSlotChanged();
               }
           });
    
           this.addSlotToContainer(new Slot(this.entityInventory, 5, 95, 8) {
    
               @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);
       }
    }
    
    

    Cela fonctionne également en multiplayer. :D</integer></integer></itemstack>