Difference between revisions of "MessageBox Tutorial"

Jump to navigation Jump to search
3,446 bytes added ,  10:37, 29 August 2007
imported>ShadowDancer
m (→‎Multiple Menus in a Spell Script: typed the wrong thing)
imported>Haama
 
==Avoiding Common Mistakes for More Complex Menus==
===Starting, stopping, and the first menu===
There are 2 and a half problems with the above script:
You won't want your menu script to run all the time, so it needs a clear beginning and a clear ending. On activators you can use the onActivate block and Activate it to start the menu:
<pre>begin onActivate
messagebox "What would you like to do?" "Button 0" ... "Button 9"
set Choosing to 1
end
 
begin GameMode
===Only use GetButtonPressed once===
if (Choosing == 1)
First, [[GetButtonPressed]] only returns the player's decision once. On this section of code
set Choice to GetButtonPressed
<pre> If (GetButtonPressed > -1)
...</pre>
If (GetButtonPressed == 0)</pre>
This works, but you will never be able to return to the first menu. To fix this, place the menu in the GameMode block, and start it up like this:
that means that, once the player has made a decision, [[GetButtonPressed]] will return '''''0''''' for the first line, but will return '''''-1''''' for the second (this would be true for an <u>'''''if/elseif'''''</u> test as well). This can be fixed by setting a variable (for this tutorial it will be '''''Choice''''') to [[GetButtonPressed]] with <u>'''''set Choice to GetButtonPressed'''''</u>. Place this line after displaying the menu, as such:
<pre>Shortbegin ChoosingonActivate
set Choosing to -1
Short Choice
end
 
begin GameMode
if (Choosing == -1)
messagebox "What would you like to do?" "Button 0" ... "Button 9"
elseif (Choosing == 1)
set Choice to GetButtonPressed
...</pre>
 
For the ending, you can set '''''Choosing''''' back to 0. However, note that the GameMode block continues to run. To reduce the number of '''if''' tests run each frame, [[Return]] if '''''Choosing''''' is 0:
<pre>begin onActivate
set Choosing to -1
end
 
begin GameMode
if (Choosing == 0)
return
 
elseif (Choosing == -1)
...
Else ;if (set Choosing ==to 1)0 or;Whenever menuyou haswant beento shownexit the menu</pre>
 
Set Choice to GetButtonPressed
 
If (Choice > -1) ;Player has made a decision
 
If (Choice == 0) ;First Option
===Keeping multiple menus separated===
;whatever you want to do
Multiple menus require careful use of a governing variable to keep the menus separate. The most common error is to place the second menu within a button response, like this:
Set Choosing to 0
<pre>begin onActivate
Endif
messagebox "What would you like to repair?" "Armor" ... "Weapons"
Else ;if (Choice <= -1) - no decision
end
Return ;Try to catch the decision in the next frame
 
Endif
begin GameMode
set Choice to GetButtonPressed
if (Choice == -1)
...
elseif (Choice == 0) ;Armor
messagebox "Which armor would you like to repair?" "Helmet" ... "Boots"
set Choice2 to GetButtonPressed
if Choice2 == -1
...
elseif (Choice2 == 0) ;Helmet
...
elseif (Choice2 == 9) ;Boots
...
endif
 
elseif (Choice == 9) ;Weapons
messagebox "Which weapon would you like to repair?" "Blade" ... "Bow"
...</pre>
If the player selects "Armor" from the first menu, the second menu ("Which armor would you like to repair") will be displayed, but any choice the player makes will really be based on the first menu. For instance, if the player selects "Boots" in the second menu, the "Which weapons would you like to repair?" menu will be displayed.
 
Following the steps of the script, you can see why. The player will be shown the "What would you like to repair?" menu. They select "Armor". For a few frames GetButtonPressed sets Choice to -1. About 15 frames after the player makes their choice, GetButtonPressed will return 0. This will bring up the second menu "Which armor...?". Choice2 will be set to -1, as the player hasn't read it yet. The player selects "Boots". Again, there will be several frames between the menu, and when GetButtonPressed returns the player's choice. This means the script will be running from the beginning of the GameMode block again. This time, Choice is set to 9 (because "Boots" was the tenth button), and the "Which weapon...?" menu is opened.
===Running after the player's decision is caught===
 
The half problem – any of your code in the <u>''''';whatever you want to do'''''</u> section will only run for a single frame. This is good enough in most cases, however, if you need to run that section for more than one frame (i.e., waiting for another process to finish) you will have to set up things a bit differently. The reason, extending on the previous reason – [[GetButtonPressed]] will only return the player's decision once, and only for one frame. To fix this, set the variable only when [[GetButtonPressed]] returns '''''-1''''', as such:
To prevent this from happening, use the Choosing variable to keep menus separate:
<pre>begin onActivate
set Choosing to -1
end
 
begin GameMode
if (Choosing == 0)
return
elseif (Choosing == -1)
messagebox "What would you like to repair?" "Armor" ... "Weapons"
set Choosing to 1
elseif (Choosing == 1)
set Choice to GetButtonPressed
if (Choice == -1)
...
elseif (Choice == 0) ;Armor
set Choosing to -10
...
elseif (Choice == 9) ;Weapons
set Choosing to -11
endif
 
elseif (Choosing == -10) ;Armor
messagebox "Which armor would you like to repair?" "Helmet" ... "Boots"
set Choosing to 10
elseif (Choosing == 10)
set Choice to GetButtonPressed
if (Choice == -1)
...
elseif (Choice == 0) ;Helmet
set Choosing to 0 ;To exit
...
elseif (Choice == 9) ;Boots
set Choosing to 0 ;To exit
endif
 
elseif (Choosing == -11) ;Weapon
messagebox "Which weapon would you like to repair?" "Blade" ... "Blunt"
set Choosing to 11
elseif (Choosing == 11)
set Choice to GetButtonPressed
if (Choice == -1)
...
elseif (Choice == 0) ;Blade
set Choosing to 0 ;To exit
...
elseif (Choice == 9) ;Blunt
set Choosing to 0 ;To exit
endif
endif
end</pre>
Note that each menu has a pair of corresponding numbers: Main menu, -1/1; Armor menu, -10/10; Weapon menu, -11/11. When you set Choosing to the corresponding number, that menu will be shown. See a better example of this in the [[Messagebox Tutorial# Moving Between Multiple Menus]].
 
 
===Running the same choice for multiple frames===
So far, we've set up the button catching section like this:
<pre>...
else ;ifelseif (Choosing == 1) or menu has been shown
Ifset (Choice ==to -1) ;Player hasn't made a decisionGetButtonPressed
if set (Choice to== GetButtonPressed-1)
return
elseif (Choice == 0) ;Player has selected the first option
...</pre>
For 99% of what you'll do, this will work perfectly. However, let's say you need to scan through the player's inventory one item at a time:
 
<pre>...
===Starting the whole thing===
elseif (Choosing == -1)
The second problem is a bit more drastic – every time the player makes a decision the menu will be displayed again. This problem can be fixed by changing the <u>'''''If (Choosing == 0)'''''</u> test to <u>'''''If (Choosing == -1)'''''</u>, and having a clear start to the script (i.e., [[OnActivate]], [[OnAdd]], etc.) that will <u>'''''set Choosing to -1'''''</u>. There are a few ways to start the script off, but my preferred method is to use a persistent activator. The advantages of a persistent activator:
messagebox "What would you like to do?" "Duplicate everything" "Nothing"
*There's a clear beginning to it ([[OnActivate]])
set Choosing to 1
*It's easy to start (unlike a quest)
set Choice to GetButtonPressed
*It's fast to start (harder for a quest)
elseif (Choosing == 1)
*It can run every frame (harder for a quest)
set Choice to GetButtonPressed
*The variables can be global (harder for a [[Unplayable Items|token]])
if (Choice == -1)
*And it's simply easier to manage than a spell (if a spell is really possible at all, I haven't seen one yet) [[MessageBox Tutorial#Multiple Menus in a Spell Script|Multiple Menus in a Spell Script]]
return
The only disadvantage is that activators need to be in the same cell as the player (loaded in memory) to run, so you will have to remember to add a few lines to move the activator to the player when starting and away when finished. Of course, with some work, quests and tokens can be made to do the same, but I don't find them quite as easy to set up.
elseif (Choice == 0) ;Duplicate items one by one from the player, into a container
set InvPos to (InvPos + 1)
set pInvObj to (player.GetInventoryObject
pContainer.AddItem pInvObj 1
...
elseif (Choice == 1) ;Nothing
set Choosing to 0 ;To exit
endif
...</pre>
"Duplicate Everything" requires several frames to complete. Once GetButtonPressed returns the player's choice, it will start off and add the first item to the container. However, on the following frames GetButtonPressed will return -1, causing an infinite loop of 'if (Choice == -1) -> return'. To prevent this, only run GetButtonPressed when it had previously returned -1:
<pre>...
elseif (Choosing == -1)
messagebox "What would you like to do?" "Duplicate everything" "Nothing"
set Choosing to 1
set Choice to -1
elseif (Choosing == 1)
if (Choice == -1)
set Choice to GetButtonPressed
elseif (Choice == 0) ;Duplicate items one by one from the player, into a container
set InvPos to (InvPos + 1)
set pInvObj to (player.GetInventoryObject
pContainer.AddItem pInvObj 1
...
elseif (Choice == 1) ;Nothing
set Choosing to 0 ;To exit
endif
...</pre>
Note that Choice is reset to -1 after displaying a menu. Otherwise, Choice would still be the same as for the last menu (let's say the player choose 0). This would not be -1, so GetButtonPressed would never be run, and that Choice (0) would run instead of the player's choice.
 
==Creating Your New Menu==
Anonymous user

Navigation menu