Jump to content

[1.14.3] Open GUI upon player-entity interaction


eafooe

Recommended Posts

Hi,

I have a custom entity with an Inventory that can hold a saddle and armor, much like a vanilla horse. I would like a GUI to open when a player right-clicks on my entity while sneaking. However, I do not want my custom entity to extend the vanilla AbstractHorseEntity class. All of the tutorials I have found for GUIs in 1.13+, however, use a TileEntity. Would anybody be so kind as to explain how this could be accomplished?

Edited by eafooe
Link to comment
Share on other sites

6 minutes ago, eafooe said:

Would anybody be so kind as to explain how this could be accomplished? 

Use Entity#processInteract to open the gui exactly how they show you for a TileEntity. Create a ContainerGui and a Container exactly how they do for a TE. They are exactly the same.

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.

Link to comment
Share on other sites

Check out the code for AbstractHorseEntity.

 

When in doubt, always read the source code. Don't rely on tutorials.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

I have read the source code for AbstractHorseEntity and have looked for other 1.13+ examples, but am still experiencing difficulties. Unlike tile entities, AbstractHorseEntity uses null for its ContainerType, which I can not get to work in forge (forge will throw an error if you say your ContainerType is null). According to this post, horse inventory handling is a special case.

 

Right now, these are my classes:

Container: 

public class MyContainer extends Container {
    // Type of container
    public static ContainerType<MyContainer> TYPE;
    // Inventory of flying pig entity
    private final Inventory chest;
    // Instance of flying pig entity
    private final AbstractRideableEntity pig;
    // Lists of acceptable saddle/armor items
    private final LinkedList<Item> saddleItems = getSaddleItems();
    private final LinkedList<Item> armorItems = getArmorItems();

    public MyContainer(int windowId, PlayerInventory playerInventory, PacketBuffer extraData){
        this(windowId, playerInventory, extraData.readInt());
    }

    public MyContainer(int windowId, PlayerInventory playerInventory, int entityId){
        super(TYPE, windowId);
        this.pig = (AbstractRideableEntity) playerInventory.player.world.getEntityByID(entityId);
        this.chest = pig.chest;

        chest.openInventory(playerInventory.player);

        this.addSlot(new Slot(chest, 0, 8, 18) {
            public boolean isItemValid(ItemStack itemStack) {
                return saddleItems.contains(itemStack.getItem());
            }

            @OnlyIn(Dist.CLIENT)
            public boolean isEnabled() {
                return true;
            }

            public int getSlotStackLimit() {
                return 1;
            }
        });
        this.addSlot(new Slot(chest, 1, 8, 36) {
            public boolean isItemValid(ItemStack itemStack) {
                return armorItems.contains(itemStack.getItem());
            }

            @OnlyIn(Dist.CLIENT)
            public boolean isEnabled() {
                return true;
            }

            public int getSlotStackLimit() {
                return 1;
            }
        });
        int i;
        int j;


        for(i = 0; i < 3; ++i) {
            for(j = 0; j < 9; ++j) {
                this.addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18));
            }
        }

        for(i = 0; i < 9; ++i) {
            this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142));
        }
    }

    public boolean canInteractWith(PlayerEntity playerEntity){
        return this.chest.isUsableByPlayer(playerEntity) && this.pig.isAlive() && this.pig.getDistance(playerEntity) < 8.0F;
    }

    public void onContainerClosed(PlayerEntity playerEntity){
        super.onContainerClosed(playerEntity);
        this.chest.closeInventory(playerEntity);
    }

    // List of acceptable saddle items
    private static LinkedList<Item> getSaddleItems(){
        return new LinkedList<>(Arrays.asList(
                Items.SADDLE
        ));
    }

    // List of acceptable armor items
    private static LinkedList<Item> getArmorItems(){
        return new LinkedList<>(Arrays.asList(
                Items.LEATHER_HORSE_ARMOR,
                Items.IRON_HORSE_ARMOR,
                Items.GOLDEN_HORSE_ARMOR,
                Items.DIAMOND_HORSE_ARMOR
        ));
    }
}

 

Screen for Container:

public class MyContainerScreen extends ContainerScreen<MyContainer> {
    private static final ResourceLocation GUI_TEXTURES = new ResourceLocation(WhenPigsFly.MODID, "textures/gui/container/flying_pig.png");
    private final AbstractRideableEntity myEntity;
    private float mousePosX;
    private float mousePosY;

    // All container-based GUI's must provide a constructor taking(T, PlayerInventory, ITextComponent), where the generic T is the type of your container object.
    public MyContainerScreen(MyContainer inventory, PlayerInventory playerInventory, ITextComponent text){
        super(inventory, playerInventory, playerInventory.player.getRidingEntity().getDisplayName());
        this.myEntity = (AbstractRideableEntity)playerInventory.player.getRidingEntity();
        this.passEvents = false;
    }

    protected void drawGuiContainerForegroundLayer(int a, int b) {
        this.font.drawString(this.title.getFormattedText(), 8.0F, 6.0F, 4210752);
        this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float)(this.ySize - 96 + 2), 4210752);
    }

    protected void drawGuiContainerBackgroundLayer(float a, int b, int c) {
        GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
        this.minecraft.getTextureManager().bindTexture(GUI_TEXTURES);
        int lvt_4_1_ = (this.width - this.xSize) / 2;
        int lvt_5_1_ = (this.height - this.ySize) / 2;
        this.blit(lvt_4_1_, lvt_5_1_, 0, 0, this.xSize, this.ySize);
        this.blit(lvt_4_1_ + 7, lvt_5_1_ + 35 - 18, 18, this.ySize + 54, 18, 18);
        this.blit(lvt_4_1_ + 7, lvt_5_1_ + 35, 0, this.ySize + 54, 18, 18);

        InventoryScreen.drawEntityOnScreen(lvt_4_1_ + 51, lvt_5_1_ + 60, 17, (float)(lvt_4_1_ + 51) - this.mousePosX, (float)(lvt_5_1_ + 75 - 50) - this.mousePosY, this.myEntity);
    }

    public void render(int posX, int posY, float p_render_3_) {
        this.renderBackground();
        this.mousePosX = (float)posX;
        this.mousePosY = (float)posY;
        super.render(posX, posY, p_render_3_);
        this.renderHoveredToolTip(posX, posY);
    }
}

 

Main Class:

 public  void registerContainers(RegistryEvent.Register<ContainerType<?>> event){
        event.getRegistry().register(IForgeContainerType.create(MyContainer::new).setRegistryName(new ResourceLocation(WhenPigsFly.MODID, "my_container")));
    }

private void clientRegistries(final FMLClientSetupEvent event){
        RenderingRegistry.registerEntityRenderingHandler(EntityFlyingPig.class, RenderFlyingPig::new);
        ScreenManager.registerFactory(MyContainer.TYPE, MyContainerScreen::new);
        LOGGER.info("Client setup method registered.");
    }

 

Usage in AbstractRideableEntity:

public void openGUI(PlayerEntity playerEntity){
        int id = this.getEntityId();
        if (!this.world.isRemote){
            WhenPigsFly.LOGGER.info("Attempting to open container...");
            NetworkHooks.openGui((ServerPlayerEntity) playerEntity, new INamedContainerProvider() {
                @Override
                public ITextComponent getDisplayName() {
                    return null;
                }

                @Nullable
                @Override
                public Container createMenu(int i, PlayerInventory playerInventory, PlayerEntity playerEntity) {
                    WhenPigsFly.LOGGER.info("Creating menu...");
                    return new MyContainer(i, playerInventory, id);
                }
            }, buf -> buf.writeInt(id));
        }
    }

Usually, I'd look at open-source mods that have similar functionality, but it looks like there is not much around for the time being.  

Edited by eafooe
Link to comment
Share on other sites

9 hours ago, eafooe said:

Usually, I'd look at open-source mods that have similar functionality, but it looks like there is not much around for the time being.  

Can you explain more on what your exact problem is? I told you that entity gui/containers function the same as TileEntity ones. The only difference is you have an Entity instead of a TileEntity.

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.

Link to comment
Share on other sites

2 hours ago, Animefan8888 said:

Can you explain more on what your exact problem is? I told you that entity gui/containers function the same as TileEntity ones. The only difference is you have an Entity instead of a TileEntity.

Here is my thought process:

 

All Containers need a constructor with these parameters: 

MyContainer(int id, PlayerInventory playerInventory, PacketBuffer extraData){
	// stuff goes here
}

 

If you want to right-click a block to pull up a GUI, you need to know the block's position. However, you still need a constructor with the parameters defined above. So, you create two constructors, one like above, and one with the parameters you actually need:

public class MyContainer extends Container{
	public static ContainerType<MyContainer> TYPE;
      
    public MyContainer(int id, PlayerInventory playerInventory, PacketBuffer extraData){
        this(id, extraData.readBlockPos(), playerInventory);
    }

    public MyContainer(int id, BlockPos blockPos, PlayerInventory playerInventory){
          super(TYPE, id);
          // more stuff here
    }
}

 

You can easily access the block's position using the pre-defined readBlockPos() method.

But I don't need to know a block's position; I am interacting with an entity, not a block.

 

Thus, I need know the id of the entity the player has interacted with and I need to access its Inventory (which can contain a saddle and armor). Right now, my actual constructor looks like this:

public MyContainer(int windowId, PlayerInventory playerInventory, IInventory chest, final AbstractRideableEntity pig){
        super(TYPE, windowId);
		// more stuff here
}

Somehow I need to be able to get my entity and its chest using data from the PacketBuffer parameter. I suppose my question is how can I store an IInventory and AbstractRideableEntity object then retrieve it using a PacketBuffer?

Link to comment
Share on other sites

2 hours ago, diesieben07 said:

Do not use IInventory. Use IItemHandler as a capability on your entity.

To send an entity over the network use Entity#getEntityId and World#getEntityByID. You do not need to send over the item handler, as it's already present as soon as you have the entity.

I've modified my code (see above) so that I am getting the entity using its integer ID. However, now I am getting 

java.lang.UnsupportedOperationException: Unable to construct this menu by type

whenever I hit this line:

super(TYPE, id);

in the MyContainer constructor. I've still yet to read up on IInventory and IItemHandler.

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.