Welcome to PenUltima Online. Click to login or register.

Gumps : The original way


.Commands and generals
.Creating a picture-gump
.Interactive gumps


.How to make a gump translucent
.How to create and use textentries

Commands and generals

Let's start with a little list of the available commands. Don't wonder about their outlook. Gumps are a client-feature and have (nearly) nothing in common with eScript.
All parameters are colored red.

  • NoMove
  • NoDispose
  • NoClose
  • Page [#]
  • GumpPic [x] [y] [id]
  • TilePic [x [y] [id]
  • ResizePic [x] [y] [gump-id] [width] [height]
  • Text [x] [y] [color] [text-id]
  • TextEntry [x] [y] [width] [height] [color] [return-value] [default-text-id]
  • Button [x] [y] [released-id] [pressed-id] [quit] [page-id] [return-value]
  • Group [groupnumber]
  • Radio [x] [y] [released-id] [pressed-id] [status] [return-value]
  • CheckBox [x] [y] [released-id] [pressed-id] [status] [return-value]
  • HtmlGump [x] [y] [width] [height] [text-id] [background] [scrollbar]
  • CheckerTrans [x] [y] [width] [height]
Those are the commands any gump consists of and which we will use during this tutorial (and you hopefully further on). Most commands are self-explanatory but since this is a tutorial I will introduce first the parameters and then all commands to you.

[#] : # represents a number, such as 0, 1, 2, 3, ... ,9. You mustn't use one number twice. Means: 1 and 11 would be ok, but 1 twice is wrong.
[x] : x specifies the x-coordinate of the upper left corner of the object you want to position
[y] : y specifies the y-coordinate of the upper left corner of the object you want to position
[id] : id represents one of three possible ids: gump-picture-ids, tile-picture-ids and text-ids. Mostly you'll receive those numbers (ids) by using Inside UO. Only exception: Text-ids, but I will explain how to get their ids, later.
[color] : color represents a color-id (which is as always an integer). Thus you can color your text.
[width] : This parameter is only used for resizepics and textentries. It sets the width of the picture or [for textentries] the width of the area in which the player may click in order to enable textentry.
[height] : This parameter is only used for resizepics and textentries. It sets the height of the picture or [for textentries] the height of the area in which the player may click in order to enable textentry.
[quit] : This parameter is only to be found in buttons. It determines whether you want to exit the gump after clicking the button or not.
[radiogroup] : This parameter represents a number (0-9) which mustn't be used twice, either.
[released-id] : This parameter sets the image you want to use for a button that is not pressed.
[pressed-id] : This parameter sets the image you want to use for a button that is pressed.
[status] : This parameter determines whether the radio-button or checkbox is already checked/pressed or not.
[return-value] : The return value is what tells the script which follows the gump what to do. A return-value for a button should never double another button. [except they are only used as "send"-buttons for radio- or checkbox values]

Now the commands...

NoMove : This command is (if neccessary) always to be found on top of a gump. It tells the client whether the player may move the gump-"window" around or not.
NoDispose : If this command is set you can not close the gump by hitting Esc (which otherwise is possible). This command should always be present. Otherwise there may occur problems.
NoClose : Adding this line to a gump means that it can not be closed by right-clicking the mouse.
Page : You may create different pages in gumps - e.g. a cook-book. Everything situated below this page-command belongs to the specified page. If another page-command follows everything below it will belong to that page ... and so on. Example:
page 0
This line belongs to page 0
This line, too
And this one
page 1
This now belongs to page 1
You should know that page 0 contains all background-commands. Means that everything you specify between page 0 and page 1 will be visible everytime the gump is open.
GumpPic : Using this command you include gump-pictures into your gump. [You best get (if you don't already have it) Inside UO which is (in my opinion) the best program for searching for ids of any kind] An example of gump-pictures? Take the spellbook - it consists of gump-pictures ... only gump-pictures. All weapons which you can see in the paperdoll belong to those gump-pictures. In Inside UO you'll find them in the (guess what?) gump-category.
TilePic : This command includes a tile-picture into your gump. Tile-pictures are graphics of those items which you can see lying on the ground ... or in a bank box. Thus you could create a picture of a bank box without having one opened (just an example).
ResizePic : ResizePics are also pics, but (as their name says) they can be resized to the width and height you like. Though you should know that only specific ids are capable of being used as ResizePics. Those are the following (Int): 83; 2520; 2600; 2620; 3500; 3600; 5054; 5100; 5120; 5170;
Text : The first 3 parameters should be clear to all of you. The 4th represents an id which is determined by a "text-array". Sounds difficult, but is very easy.
Gumps are constructed in two arrays [which we will see when building our first gump]: a layout-array and a text-array. The 4th parameter thus is the number of the chosen text-piece in the text-array. I think an example is better [now you'll see how a gump-script will look like, but I'll keep it very simple]:
var gflayout := {
		"page 0",
		"text 100 100 0 0" // last parameter = text-id
var gfdata := {
		"This is the first textline"
You see, the last parameter is 0 and therefore means that the text which is to be found in the data-array on position 0 will be shown at position x=100, y=100 in color 0 (black). Yes, the first position in an array is 0 [for gumps] - not 1 as usual in eScript. [as I said - they have nearly nothing in common]
TextEntry : This command allows you to define an area in which a player can click and then enter a text of his choice.
Button : This command is used in nearly every interactive gump. You can switch to other pages or execute specific actions with them. If you want to access another page, "pressed-id" and "return-value" have to be set zero and the page-id gets set the number of the page to which should be switched. If you want to execute an action you have to set "page-id" zero, mostly "quit" to 1 and then enter a specific value for each possible action. [Don't worry if you don't understand this at the moment, we will handle interactive gumps later]
Group : Group is used like "page", except the fact that group defines a radio-group and no page. Thus you can press many radio-buttons on one page without the need of checkboxes (which are mostly more complicated).
Radio : Radio creates a radio-button, which is just like a normal button, except it just adds a value to those which will be returned to the script if a "normal" button is pressed.
CheckBox : The same as radio-buttons except you can check many boxes instead of just one.
HtmlGump : As the name indicates, this command allows you to use html-commands. Actually it creates a text-area, either with a background or without it and with or without a scrollbar. Additionally you pass a text-id - and this text can be formatted in the following ways:
<center> = means "center" - the text in between <center> and </center> is always in the middle of your text-area
<br> = means "break" - the text goes on in the next line (this one does not need a "</br>" at the end)
<b> = means "bold" - the text in between <b> and </b> is displayed in bold letters
<i> = means "italic" - the text in between <i> and </i> is displayed in italic letters
<u> = means "underlined" - the text in between <u> and </u> is underlined
One example for a html-formatted data-array-entry:
var gfdata := {
	      "<center>Hello guys!</center>;<br>
	       This one is a html-gump.<br>
	       <b>o</b>Html-tag supported
	       <b>o</b>Real Scrollbar
	       <b>o</b>Nice background ;)"
One thing you can actually not do [or I just don't know how] : Change the color of your text. It is always black. [If htmlgump does not put out what I mention here, you may be using a client that is too old. The only chance is to use a more up-to-date one]
CheckerTrans : Checkertrans creates transparent gumps. Any picture or resizepicture you defined before "checkertrans" will become transparent in the area you define with checkertrans. [The same as above: You need a client about 3.x.x in order to be able to see an effect when using checkertrans]
An example:
var gflayout := {
		"page 0",
		"resizepic 100 100 5054 100 200",
		"checkertrans 110 110 80 180"
That would create a transparent resizepic with a full visible border. [I'm not totally sure about its outlook, but you'll get the idea of the effect]

Now some generals you should always remember:
  • Gump-scripts are not case-sensitive
  • You may only use Integers in gumps - no hex [Thus you should have this tool]
  • Page 0 is the background of each gump and thus always visible
  • When opening a gump page 0 and [if specified] page 1 will be visible
  • Don't forget the quotation-marks around each line of gump-script and the comma at the end [Only at the end of each gump-array you may leave out the comma]
  • You best name the gump-arrays "gflayout" for the layout-array and "gfdata" for the data-array. Why? Because in this way you may write your gump in "the original way" but also have the possibility to use "GFFunctions" [GF-Functions always use "gflayout" and "gfdata" as names for their arrays]
  • You must not ever reverse the usual order of textlines. Means: After line 0 follows 1, then 2, then 3 and so on. You may not put textline 2 after textline 3! This always creates an error which kills "the players" client.
    Here an example:
    var gflayout := {
    		"page 0",
    		"text 100 100 0 0",
    		"text 100 115 0 1"
    This is correct.
    var gflayout := {
    		"page 0",
    		"text 100 100 0 1",
    		"text 100 115 0 0"
    This is incorrect.
  • The amount of space between two textlines [one below another] should be 15
  • Never double return-values. [It often causes problems when you want to have an accurate result]
  • The return-value of the pressed button is always to be found in sendgump[0].
    An example:
    var choice := SendDialogGump( who, gflayout, gfdata );
    var pressed_buttons_id := choice[0];

Creating a picture-gump

Now this was all very theoretical. Now we'll experiment with these things. I guess most of you will start here and take a look at the descriptions later. I mentioned some of these things before, but repetition did never harm anyone.
So, a gump is constructed in two arrays. [You should already know arrays and how to handle respectively create or modify them. If not please read Rac's EScript-guide first!]
Normally you just write value after value in an array but to make it more clearly you should put them one below another - just like this:

var gflayout := {
		"page 0",
		"text 100 100 0 0",
Thus you'll get a better overview and can add comments more easily.
Now one picture follows which represents the gump we want to have when we finish this part of the tutorial [Creating a picture-gump] - note, that this is only one page out of two. I just want to keep the second one secret ;) :

page 0 and page 1

Well - each gump mostly starts with one of the first three commands from the list on top of this document. It's optional to add them but mostly you will want the player to make a choice. [Though you should know that you can also cancel a gump by exiting UO. To prevent this you'll have to add props to the char and maybe jail him until he makes his choice. But this doesn't belong to this topic and will be up to your fantasy.]
Then you create the first page [page 0] which represents the background of every following page - mostly you'll put in pictures and some text-lines here.
Now let's try to build the gump shown above. "The player" should look at all pages, so you have to forbid right-click-closing and quit by esc. Also he shouldn't move the window around, because the gump is very interesting so he should focus on it. [You smell a bit of irony? Aw! ;) ] Now our script would look like this [Don't worry - there are comments and I will explain the difficult parts again below]:
use uo;
use os;

include "include/attributes";

program gumptest( who )

var gflayout := {
		"noclose",   // close by right-click forbidden
		"nodispose", // you did not want to hit esc, did you? 
		"nomove",    // you musn't move the window around
		"page 0",    // we initialize the first page which is the background
    "resizepic 100 100 3600 300 300", // this is the dark background
		"text 145 120 32 0",  // the headline "my first picture-gump"
		"page 1",	      // now we initialize the second page ...
		"text 120 160 50 1",  // ... and create some text-lines...
		"text 120 175 50 2",
		"text 120 190 50 3",
		"text 120 205 50 4",
		"text 120 220 50 5",
		"text 120 250 50 6",
		"text 120 265 50 7",
		"button 240 360 5540 5541 0 2 0", // the button to switch to page 2
		"page 2",	      // we initialize the third page (page 2)
		"text 140 230 50 8",  // the last text-line ...
		"button 188 230 249 248 1 0 0"  // ... in which we place a button 
var gfdata := {
		"<[ My first Picture-gump ]>",	 // these are only textlines which are used
		"Your skin-color  : "+who.color, // in the layout-description above, namely
		"Your gender     : "+who.gender, // in order from top to bottom.
		"Your strength   : "+getattribute(who,ATTRIBUTEID_STRENGTH),
		"Your dexterity   : "+getattribute(who,ATTRIBUTEID_DEXTERITY),
		"Your intelligence : "+getattribute(who,ATTRIBUTEID_INTELLIGENCE),
		"Click the arrow to proceed",
		"to the next page",
		"Press  [okay]  to exit"	  // the text around (and behind) the okay-button

SendDialogGump( who, gflayout, gfdata );

Well - read the comments carefully? [If not, do it now, please] Then you should have understood the main "body" of a gump. You always need "page 0" - the rest is optional. [Though a gump without content seems to be a bit useless, don't you think?]
Now the full dialog in short words:
  • Opening layout-array
  • Disabling close through right-click
  • Disabling close through Esc
  • Disabling the possibility to move the window
  • Initializing first page (background / page 0)
  • Writing content of background (headline + picture)
  • Initializing second page (start-page / page 1)
  • Writing content of second page (textlines + page-button)
  • Initializing third page (page 2)
  • Writing content of third page (textline + exit-button)
  • Closing layout-array and opening data-array
  • Writing content of text-lines
  • Closing data-array and sending the gump to the player
I hope everyone now knows how to write a simple gump. Then we'll try to create one of those tricky button, radio and checkbox-gumps and take a look at how to handle their output.

Interactive gumps

Ok, we should first make clear what interactive gumps are: I see an interactive gump as a gump which "communicates" with a player and returns his choice so the script does what he wants it to do. Take the hair-dye-gump as an example of such a gump.

You already know how to write a (rather simple) gump - but that's only a piece of what you can do with gumps. You can create multiple-choice menus, election-gumps, shopping-lists, theater-bills and so on ...
So - how do we get to know what the player wants? First we have to make the send-part of our program a variable so we can refer to it, after the player made his choice. Like this:

var his_choice := SendDialogGump( who, gflayout, gfdata );
Easy? Yes!
Now we have to know how to get his choice out of that variable. This is nothing you can know - it's just like this - all keys are saved in one part of that variable, called "keys". These keys build an array - more returned-values - more keys. To access these keys you use some line like this:
var his_choice := SendDialogGump( who, gflayout, gfdata );
var keys := his_choice.keys;
Now you would have the whole key-array in a new array, called keys - but this is no use. We have to split the keys and get the right one so we can make the script act in the way we want it to.

If you don't return any key [e.g. you close the gump by right-click without having touched anything] the length of the key-array is 0 and you won't find any key in it. On the other side if you push a button which's return-value is not 0 or choose a radio/checkbox your key-array would have a length of 2 or 3 or 4 [or higher]- depending on how many boxes you choose and whether you press a button or not.
There's a certain order in which the different keys are to be found in the array namely from the low values to the high values. An example:
You have one button whichs return-value is 1000 and 2 radio-buttons the return-values of which are 500 and 1500. If you now press the 500-valued radio-button and the "normal" button, the output would look like this:
keys[0] = 0	// always empty
keys[1] = 500	// 500 < 1000; therefore you'll find the radio-value here
keys[2] = 1000	// 1000 > 500; thus the button's key has got this place
If you press the 1500-valued radio-button and the "normal" button the output would look like this:
keys[0] = 0	// always empty
keys[1] = 1000	// 1000 < 1500; therefore you'll find the button-value here
keys[2] = 1500	// 1500 > 1000; thus the radio's key has got this place
I bet you all see the difference.
To make things easier you best make the important values the highest. Thus you can use a loop like the following to read out the important value:
var choice;
foreach key in keys
	choice := key;
After this loop you would have the highest value in the variable choice. Then you can work with it, create a case-statement or many ifs - it's up to you.
To make things really clear I will give a complete example:

use uo;
use os;

program gumptest( who )

var gflayout := {
		"page 0",
		"resizepic 0 0 5170 200 300",
		"radio 100 100 5540 5541 0 100", // All radio-values are important and thus
		"radio 100 120 5540 5541 0 200", // greater than any button-value [in this example: 1]
		"radio 100 140 5540 5541 0 300",
		"button 100 180 249 248 1 0 1"
var gfdata := { };

var choice := SendDialogGump( who, gflayout, gfdata );
var his_choice;
foreach key in ( choice.keys )
	his_choice := key;

var output;
case ( his_choice )
	100: output := "The player chose the button the value of which is 100";
	200: output := "The player chose the button the value of which is 200";
	300: output := "The player chose the button the value of which is 300";
	default: output := "The player must have canceled the gump without doing anything";
	// default means if the value is neither 1 nor any of the radio-values
	// [so the player canceled the gump without doing anything]. In this example it doesn't matter
	// whether you press the okay-button or not, the values are counted anyways. This is because
	// it is not important for this example. If you're writing a gump and want it to work correctly
	// you should embed a test if the okay-button was pressed
	// [like this: if ( choice[0] == 1 ) <- then the player has pressed the okay-button.
	// Why? You should know]

SendSysMessage( who, output );

This would be a working gump. Depending on what the player chooses one of the given messages will appear. Why? That is what you should already know. I only wrote this example so you can see how a working gump with radios looks like. Sometimes some expressions are not comprehensible at once. So the code should explain what maybe some of you didn't understand exactly.

But what do we do if we have not only one button and/or radios, but checkboxes? Using checkboxes [or radios plus radiogroups but this will follow later (or earlier - I'm not ready with the tut ;)] we will receive many values in [maybe] a more or less great amount of keys. Then it won't be enough if we get one value [key]. We have to get them all. Thus we will use the loop which we used above - only slightly changed. Look here:
var choice;
foreach key in keys
	choice := choice + key;
Now we add each key to the new variable, called choice. If it was enough to look at one radio/button -value [see example-code above] you now have to ask for a sum of all values respectively all possible values.
Example: [button return-value] + [checkbox-value] + [checkbox-value] = [value to ask for].
This is surely more complicated but [as nearly always] more features require more work.
In order to clarify things I will give an example of a checkbox-program:
use uo;
use os;

program gumptest( who )

var gflayout := {
		"page 0",
		"resizepic 0 0 5170 200 300",
		"checkbox 100 100 5540 5541 1 100", // I set the "status"-parameter to 1
						       // In this way the player has to make a decision
		"checkbox 100 120 5540 5541 0 200",
		"checkbox 100 140 5540 5541 0 400",
		"button 100 180 249 248 1 0 1"
var gfdata := { };

var choice := SendDialogGump( who, gflayout, gfdata );
var his_choice;
foreach key in ( choice.keys )
	his_choice := his_choice + key;

var output;
// Now you either have to create a formula or you must calculate
// all possible values and then ask for them. I'll use the second method now
// CB = Checkbox
case ( his_choice )
	101: output := "CB 1 + Button";
	201: output := "CB 2 + Button";
	301: output := "CB 1 + CB 2 + Button";
	401: output := "CB 3 + Button";
	501: output := "CB 1 + CB 3 + Button";
	601: output := "CB 2 + CB 3 + Button";
	701: output := "CB 1 + CB 2 + CB 3 + Button";
	default: "output: This should not have happened";

SendSysMessage( who, output );

Whenever you press a checkbox + button in this gump you should receive a message what you've done. [It does not work? Tell me!]

How to ... create a translucent gump

You all know that you can create gumps which are translucent [yes you do - at least now :)]. Those gumps were introduced with [I'm pretty sure ;)] UO Renaissance. Thus you can only use that feature if you use a client > 3.0.6 [If it's not exactly that version, try another - at least with 3.0.8j it's definitely possible!]. If you don't have the needed client, UO will ignore the command and display everything "as usual".

Creating such a gump is pretty easy - here's an example of such a thing:
use uo;
use os;

program translucent_gump_test( who )

var gflayout := {   "page 0",
                    "resizepic 50 80 2620 400 40",  // This resizepic is completely translucent
		    "text 65 86 2101 0",            // This text is translucent, too
                    "checkertrans 56 86 386 28",
		    "text 250 86 2101 1"            // This text is 100% visible

var gfdata := { "Durchsichtiger Text",
		"Undurchsichtiger Text"

SendDialogGump( who, gflayout, gfdata );

Well - why does the gump appear as it does? Simple explanation: The checkertrans-command defines an area - using the given parameters - and makes everything which lies in that area and which has been defined before it, translucent. Of course you may create those translucent areas as big or small as you like. Is that everything? Yes, it is!

How to ... create and use textentries

Some people asked how to create textareas so here's an example and - as usual - an explanation afterwards:

The following script will - after having been compiled and executed - print, what you typed into the given textarea if you right-click the gump-window.
use uo;
use os;
use util;
use cfgfile;

program textentry_gump( who )

var gflayout := {   "page 0",
                    "resizepic 50 80 2620 400 110",
                    "checkertrans 56 86 386 288",
                    "text 160 100 2101 0",
                    "TextEntry 150 130 200 20 40 0 1"     // An explanation of the parameters can be found in this chapter

var gfdata := { "Textentry-Gump-Example",
                "YOUR TEXT HERE"     // The default text of the textentry

var result := SendDialogGump( who, gflayout, gfdata );

sendsysmessage( who, "You typed: "+result[0][4, len(result[0])] );

Short info for the script above: As to the textentry, all things that are connected to each other are marked in the same colors.

The result of the textentry is to be found as the first key in the result-array of the gump, because we have no button, nor any radios etc. etc. The "connection-colors" should clarify how the numbers are connected.
We just fetch the data from the textentry - then we could use it if only UO wouldn't send the textentry-id in front of it. So we cut it off via [4, len(result[0])] [As far as I know this method is also mentioned in Rac's EScript-guide, but I'm not sure]. Thus we get all characters from the fourth on until the end of the string.

Although this doesn't really belong here, I'll explain the "cut-off"-method:
The formula: stringvar[from_char, how many chars after it?].

An example:
var mystring := "I ate a hotdog";
    mystring[9, 3] := "";
Now mystring contains: "I ate a dog".

Author: Lystramon (~lys)
E-mail: Lystramon@web.de