Résolu É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>