Tutorial by: DereWah


Anvil GUIs in Skript

Anvil GUIs are really useful for reading user inputs without having the user to open the chat. These type of GUIs are already used often in plugins but in Skript there hasn't ever been a consistent way of creating and reading one. Today, it's possible, thanks to an addon I created: Skript-AnvilGUI.

In this Tutorial we'll go through how an AnvilGUI can be created, modified and even changed.

Creating an AnvilGUI inventory and displaying it

Let's start with the basics: before being able to open an anvil gui, we need to actually define it. To define the anvil GUI, you'll need to specify the following values:

To do so, you can use the expression

command /openanvil:
    trigger:
        set {_gui} to a new anvil gui named "Enter Your Name" with default text "Type here"
        # continues below

Now that we have created a reference of the Anvil GUI, we can actually show it to a player. Note that for now, we still are not implementing any click behaviour on the GUI.

To open it, we can simply use

command /openanvil:
    trigger:
        set {_gui} to a new anvil gui named "Enter Your Name" with default text "Type here"
        open anvil gui {_gui} to player

As you probably know, to accept input there must be at least an item in the Anvil GUI, but we didn't specify any. In this case, 1 paper will occupy the input slot of the GUI. Obviously this item (and all the other slots) will be non-interactable to prevent stealing or duping.

Setting Anvil GUI Items

Now let's see how to add items in the GUI. The base expression is

(left|right|output) (item|slot) of %virtualanvil% 

As you can see, you can choose in between the left, right, and output of the anvil GUI. Note that as of right now (and because of how MC works), the output slot will always be overridden by the input (left) slot. (Because that's how an anvil works)

command /openanvil:
    trigger:
        set {_gui} to a new anvil gui named "Enter Your Name" with default text "Type here"
                set left item of {_gui} to 1 diamond block named "This will be overridden! You won't see this!" with lore "&aClick to accept."
        open anvil gui {_gui} to player

Let's carefully analyze what I wrote in the item syntax. This is more of a "smart" trick that I'll teach you, but it's not strictly necessary. In anvil GUIs, when items are set in the slots, the item name will always be overridden with what the user is typing in the input field. Therefore, if you want to nicely show, in the output slot, a message such as "Click to confirm", you can't do it by writing text in the name of the item.

The workaround is actually nice, you can edit the 1st line of the lore to show "&aClick to accept/confirm/what you want". The lore will not be edited while using an AnvilGUI, and actually you'll be in a situation where You'll see what the user wrote in the input field, and right below it the accept message.

Handling user interactions

Let's now proceed and decide how to handle user interactions, such as clicks. With this addon, you'll be able to handle 2 types of interactions:
1- anvil gui close
2- anvil gui click

The names are pretty self explanatory. Let's see how to make an uncloseable anvil gui. We basically listen for an anvil gui close event (which is called whenever any anvil gui is closed), we check for the correct anvil gui name, and we cancel the event.

Anvil Gui Close

<pre><code>    on anvil gui close:
        if title of event-virtual anvil is "You can't close this unless you type 1234":
            if event-text is not "1234":
                cancel event
</code></pre>

The title check

if title of event-virtual anvil is "You can't close this unless you type 1234"

is extremely important! Do not forget this part! If you don't check for the title, you'll end up making ALL the anvil guis non-closeable. If there are any other skripts on the server that are using this addon, you'll end up breaking them, which is definetly not good.

In the on anvil GUI close, we can access 2 things:

The text, accessed as event-text, is what the user has written in the anvil gui when the close event was fired. In the snippet I check if in the input field there is written "1234". In case anything else than 1234 is written, I cancel the event, making it impossible to close the gui without typing in it.

IMPORTANT SIDE-NOTE!
While making your own skript you might encounter some weird behaviour, (which is actually standard behaviour, but unexpected if not known), so I'll try to inform you about this small thing: If an event happens, and the input field of the anvil gui is left untouched (basically is identical to the default text you specified in the creation of the anvil gui), the event-text will return an empty string ("").

Basically, the cases in which the Anvil GUI text is empty ("") or is the default text, whill return the identical empty string. This is not that big of a deal, but it is indeed unexpected behaviour, as you'd normally expect the text to be the default string if left untouched.

Anvil Gui Click

Let's now see the big thing of this addon: handling clicks. Below you can see a simple skript that asks a user for a secret password, in this case 1234.

        command /password:
            trigger:
                set {_gui} to a new anvil gui named "&0Insert password" with default text "password"
                open anvil gui {_gui} to player

        on anvil gui click:
                            if title of event-virtual anvil is "&0Insert password":
                                    if event-integer is 2: #clicked the output item slot
                                            if event-text is "1234":
                        close player's inventory
                        send message "&aCorrect password."
                    else:
                        set text of event-virtual anvil to "Wrong password"
                        open anvil gui event-virtual anvil to player #reopen the anvil gui, but with a different text.

In the anvil gui click, we first should check the name of the anvil gui, for the same reasons I stated above. Again, this is really really important.
Afterwards, you can see that we check for a thing called "event-integer". This is simply the ID of the slot that has been clicked in the event. The slots are mapped as follows:

Slot clicks are detected even if the slot is empty. In the skript above we check for the slot 2, which means the output slot.
We can do anything with these clicks, cancel the click event, close the anvil gui and show a message, call a function, etc.

Let's now handle the case in which the password input is wrong, or we get any unexpected text in the anvil gui. Wouldn't it be nice to, instead of just flicking the GUI and cancelling the event, we could show in the input field some text, such as "Invalid password"?

To do so, we can't simply overwrite the input text. Code like this

set event-text to "Invalid Password"

Would not work, as we are not updating the anvil gui we are showing to the player. When an anvil gui is clicked, in fact, we access a "copy" of that anvil gui, but not the live instance that is open to the player. Editing that copy will not affect the anvil gui that is being shown to the player. To overcome this, we simply can create a new anvil GUI, identical to the first, and re-open it to the player. Let's analyze the code:

                        set text of event-virtual anvil to "Wrong password"

This basically takes the event-virtual anvil, which as stated above is a COPY of the anvil gui that has been clicked, and changes its (default) text property. Note that this is different from the "event-text"! The event-text is what was written in the event being called. this "text" of event-virtual anvil is instead the default property of the anvil gui. If we were to read this "text of event-virtual anvil" before changing it, we'd read "password", because above, when we were creating the anvil gui for the first time we wrote

set {_gui} to a new anvil gui named "&0Insert password" with default text "password"

Now that we have changed the text, we need to show this new anvil gui to the player. To do so it is simply a matter of opening it as we always did:

set text of event-virtual anvil to "Wrong password"
open anvil gui event-virtual anvil to player #reopen the anvil gui, but with a different text.

The player will just see the normal anvil gui he was looking at earlier, but with a different input field. This is because we took a copy of the anvil gui he interacted with in the first place. (Therefore same title, same items with the same lore, etc.)

Persistent Data - Stacking multiple Anvil GUIs inputs one after another.

The next part is going to be pretty technical, so if you're not really familiar with Skript it might be pretty hard. I will explain how to handle cases in which you need to open multiple anvil GUIs one after another, in order to accept multiple inputs from a player. Let's say you want to make a Skript that Bans a player using Anvil GUIs. When an admin wants to ban a player, he types /bangui, types in a first Anvil GUI the Name of the player, and in the 2nd gui a Reason.

As you can imagine, after the 2nd anvil gui click is performed, (the admin has specified the reason of the ban), we need to remember also what they wrote in the 1st anvil gui, to actually ban that player. The neatest way to handle this, is using Items lore. (In standard Skript I would use NBT Tags, but unfortunately NBT tags of items in Anvil GUIs get reset each time, and are not editable)

The workflow is:

In this first part of the click event, I handled the "insert nickname" part. As you can see I check for the correct item slot click, I check that the text is not empty/default ("") and then I proceed with my desidered behaviour.
As you can see, I store the name of the player I want to ban in a local variable (just for convenience). Now, I need to pass onto this "target" variable, and make sure it will be accessible on the next anvil gui click, where the reason field will be shown.

Since I am creating and opening this "reason" anvil gui, (because I want to show it right after having input the name), I will put in the item slot a custom item, and write in its lore the nickname to ban. This is the way to pass-onto the text I wrote in this 1st gui to the 2nd. As you can see I write it in black (&0) to hide it from the player (black is not really visible in lore unless you have a custom resource pack, but anyways this is not really important. Just for aesthetic. Also I put it in the 2nd line of the lore (this is very important), as in the first I want to show the pretty "&aClick to proceed" line.

Now, when the new anvil gui is clicked, we retrieve the name, which will be exactly in the 2nd line of the lore:


    else if event-title is "&0Insert reason to ban":
        if event-integer is 2:
            if event-text is not "":
                set {_reason} to uncoloured event-text
                set {_leftitem} to left item of event-virtual anvil
                set {_target} to uncoloured 2nd line of {_leftitem}'s lore
                make player execute "/ban %{_target}% %{_reason}%"
                close player's inventory

As you can see we access the item of the anvil gui, and of its lore we access the 2nd line (uncoloured, because earlier we made it black, and we wan't to ban "player", not "&0player".

Finally we perform the ban on behalf of the player. As you can imagine, this workflow is stackable. In here we just have 1 value that we want to carry to the 2nd anvil gui. You can make as many "carries" as you want, as long as you do it correctly, by putting each value on a different line and remembering its position.

If you want a little challenge, to see if you got this, try to add on top of this workflow a new anvil GUI, to allow the admin to specify the amount of time the player will be banned for. You'll have to carry to the 3rd anvil gui the 2 previous values input by the admin (nickname & reason), in the lore of an item, and then read them back in the new gui.

A final disclaimer about this section, I don't use variables for this part because, well variables are persistent, sure, but think about it: if a player disconnects in the middle of the workflow, you'd have to go back and clean all the previous fields chosen by the player. Also you'd have to create a new var storage for each player, as you might have multiple admins using the GUI at the same time. With items lore you have "private access" for each player to an item only they can see, and whenever that item is "lost", for example the GUI closed or the player disconnects, you don't need to perform any memory cleanup.

Conclusion

I hope this tutorial has been an useful resource to you. If you have any feedback, find any error, feel free to leave a comment or contact me (Discord/Telegram @DereWah).
Have fun!


Did you find DereWah's tutorial helpful?


You must be logged in to comment