User:Haama

From the Oblivion ConstructionSet Wiki
Revision as of 20:37, 13 June 2007 by imported>Haama
Jump to navigation Jump to search


Rough Draft of my Messagebox Tutorial (so I can look at it, etc.):

Most of your menu needs can be taken care of with a simple token script:


Begin onAdd
  Messagebox "Your message" "Some options" "Some more options"
End

Begin GameMode
  set Choice to GetButtonPressed
  if (Choice == 0)
    ;do stuff
    RemoveMe
  Elseif (Choice == 1)
    ;do stuff
    RemoveMe
  Endif
End


Just add the token to the player whenever you want to run the menu, but be aware that this script is severely limited in functionality. There are many ways to do menus, so this guide will lead up from the simplest menu (Done!) to a fully featured menu (well, featured enough for me) that will be able to: run multiple menus from one script, run the same choice for multiple frames, make it very easy to cut and paste for each new menu, as well as some extras. Along the way I'll be focusing on some common mistakes and how to avoid them.

First, some basic information: There are two sides to every menu – the display of the messagebox and catching the player's decision. You display the menu with the function "Messagebox" and catch the player's decision with the function "GetButtonPressed". "Messagebox" takes one frame to display, so you can use any block to display it. However, "GetButtonPressed" needs to be in a block that continuously runs (i.e., GameMode, MenuMode, ScriptEffectUpdate) and needs to be on a script that is running nearly every frame (an activator that is in a loaded cell, a quest running every .001 seconds, etc.). The reason for this – in order to read it, make a decision, click the option, and let go of the mouse button it will take the player longer than a frame, so you need to run the script every frame until then. Another oddity - "GetButtonPressed" returns numbers for each decision: -1 means no decision has been made, 0 means the player selected the first option, 1 for the second, etc. (you can have 10 options at most). With those in mind, here's a basic menu script. (You may also notice that it doesn't quite work, that'll be explained afterwards.)


Short Choosing


Begin GameMode

 If (Choosing == 0)
   Messagebox "What would you like to do?" "First Option"
   Set Choosing to 1
   Return
 Else
   If (GetButtonPressed > -1) ;If it's still -1, then the player hasn't made a choice yet
     If (GetButtonPressed == 0) ;First Option
       ;whatever you want to do
       Set Choosing to 0
     Endif
   Else ;if (GetButtonPressed <= -1)
     Return ;Try to catch the decision in the next frame
   Endif
 Endif

End


The variable "Choosing" is used to separate the display of the menu and the catching of the player's decision. Since it's in a GameMode block, the script will run every frame. GetButtonPressed will return -1 until the player makes a decision, in which case it will return 0 or 1 (well, kind-of, wait a sentence), depending on the player's decision.

There are 2 and a half problems with this script: First, GetButtonPressed only returns the player's decision once. On this section of code


   If (GetButtonPressed > -1)
     If (GetButtonPressed == 0)


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 'if/elseif' test as well). This can be fixed by setting a variable to GetButtonPressed (i.e., 'set Choice to GetButtonPressed') near the top of the GameMode block and then running the 'if' tests on the variable (Choice) instead of GetButtonPressed.

The half problem – any of your code in the "whatever you want to do" 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, again – GetButtonPressed will only return the player's decision once. To fix this, only set the variable ('set Choice to GetButtonPressed') when GetButtonPressed returns -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 'If (Choosing == 0)' test to 'If (Choosing == -1)', and having a clear start to the script (i.e., onActivate, onAdd, 'if Choosing == 0' -> 'set Choosing to 1') that will 'set Choosing to -1'. There are a few ways to do this, but my preferred method is to use a persistent activator. The advantages to a persistent activator: there's a clear beginning to it (onActivate), the variables are global (unlike a token), it runs every frame (unlike a quest), it's easy and fast to start (unlike a quest), and it's simply easier to manage than a spell (if a spell is really possible at all, I haven't seen one yet). The only disadvantage is that 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.)

You'll need to set up some objects for the next script: an invisible activator, an XMarker, and your own cell: To make your own cell scroll down the "Cell View" window and select "TestQuset01", right-click it and select "Duplicate Cell". Rename your new cell to something you'll remember (and don't worry about the lack of floors, it'll work just fine). To make the XMarker, scroll down the "Object Window", select Statics, and scroll to the bottom. Double-click your cell in the "Cell View" window to open it in the "Render Window", and drag the XMarker from the "Object Window" into the "Render Window". Right-click the red X in the "Render Window", and select edit. Now, give it a name you'll remember in the "Reference Editor ID" box (in these examples it will be "YourXMarker"). Finally, make the activator by selecting an activator in the "Object Window", edit the name, press enter or select ok, and click "Yes" when it asks if you want to create a new item. Now, drag your new activator into the "Render Window". Right-click it, give it a "Reference Editor ID", mark it as "Persistent Reference", and "Initially Disabled". Place this script on your new activator:


Short Choosing Short Choice


Begin onActivate

 Set Choosing to -1
 If (GetInSameCell player == 0)
   MoveTo player
 Endif

End


Begin GameMode

 If (Choosing == 0) ;meaning it shouldn't be running
   If (GetInSameCell YourXMarker == 0)
     MoveTo YourXMarker
   Endif


 Elseif (Choosing == -1) ;Display your menu
   Messagebox "What would you like to do?" "First Option" "Second Option"
   Set Choosing to 1
   Set Choice to GetButtonPressed
   Return
 Elseif (Choosing == 1) ;Catch the player's decision
   If (Choice == -1) ;No choice yet
     Set Choice to GetButtonPressed
     Return
   Elseif (Choice == 0) ;First Option
     ;run your code for the first decision
     Set Choosing to 0 ;to finish up
   Elseif (Choice == 1) ;Second Option
     ;run your code for the second descision
     Set Choosing to 0 ;to finish up
   Endif
 Endif

End


Ok, no games that time. Activate your activator from any script (YourActivatorsReferenceEditorID.Activate player, 1) and the above code will do the rest. Not only that, but it makes multiple menus easy to do. Remember that each menu has two parts: the display of the menu and catching the player's decision. So, each menu can be broken into two numbers, a negative number (-1 in the example above) and a positive number (1 in the example above). Use different numbers for each menu, and whenever you want to move to a new menu, use 'set Choosing to -#'. Here's an example:


Short Choosing Short Choice


Begin onActivate

 Set Choosing to -1
 If (GetInSameCell player == 0)
   MoveTo player
 Endif

End


Begin GameMode

 If (Choosing == 0) ;meaning it shouldn't be running
   If (GetInSameCell YourXMarker == 0)
     MoveTo YourXMarker
   Endif


 Elseif (Choosing == -1) ;Display your menu
   Messagebox "Would you like to donate gold or food?" "Gold" "Food" "Blood" "Cancel"
   Set Choosing to 1
   Set Choice to GetButtonPressed
   Return
 Elseif (Choosing == 1)
   If (Choice == -1) ;No choice yet
     Set Choice to GetButtonPressed
     Return
   Elseif (Choice == 0) ;Gold
     Set Choosing to -2 ;to open the Gold menu
   Elseif (Choice == 1) ;Food
     Set Choosing to -3 ;to open the Food menu
   Elseif (Choice == 2) ;Blood
     Set Choosing to -4 ;to open the Blood menu
   Elseif (Choice == 3) ;Cancel
     Set Choosing to 0 ;to close the menus
   Endif
   Return
 Elseif (Choosing == -2) ;Gold menu
   Messagebox "How much Gold would you like to donate?" "25" "I've changed my mind" "I've changed my mind, I'll donate Food" "I've changed my mind, I'll donate Blood" "I've changed my mind, I won't donate anything" 
   Set Choosing to 2
   Set Choice to GetButtonPressed
   Return
 Elseif (Choosing == 2)
   If (Choice == -1) ;No choice yet
     Set Choice to GetButtonPressed
   Elseif (Choice == 0) ;25
     If (player.GetGold > 25)
       Player.RemoveItem Gold001 25
       Set Choosing to 0
     Else
       Set Choosing to -99 ;a message that the player doesn't have enough
     Endif
   Elseif (Choice == 1) ; I've changed my mind
     Set Choosing to -1 ;to return to the opening menu
   Elseif (Choice == 2) ; I've changed my mind, I'll donate food
     Set Choosing to -3 ;to open the food menu
   Elseif (Choice == 3) ; I've changed my mind, I'll donate blood
     Set Choosing to -4 ;to open the blood menu
   Elseif (Choice == 4) ; I've changed my mind, I won't donate anything
     Set Choosing to 0 ;to close the menus
   Endif
   Return
   Elseif (Choosing == -3) ;Food menu
     Messagebox "How much food would you like to donate?" "Options" "More Options"
     Set Choosing to 3
     Set Choice to GetButtonPressed
     Return
   Elseif (Choosing == 3)
   If (Choice == -1) ;No choice yet
     Set Choice to GetButtonPressed
   Elseif (Choice == 0)  ;Options

   Endif
   Return


   Elseif (Choosing == -99) ;Special menu that tells the player they don’t have enough
     Messagebox "You don't have enough."
     Set Choosing to 99
     Set Choice to GetButtonPressed
     Return
   Elseif (Choosing == 99)
   If (Choice == -1) ;No choice yet
     Set Choice to GetButtonPressed
   Elseif (Choice == 0) ;player pressed "Done", return to main menu
     Set Choosing to -1
   Endif
   Return


 Endif

End


I suggest using numbers instead of other variables when setting "Choosing". Numbers give more meaning than words in this case, as the negative and positive numbers separate which part of the menu you're dealing with. You can also use numbers to signify which layer of the menu you are in. For instance, -1 was the first layer in the above example. For the sub-menus of the main menu (Gold, Food, Blood), you can use -11, -12, -13. And, for example, for the sub-menus of Food you can use -121, -122, -123 such that the first number signifies the menu of the first layer, the second the menu of the second layer, etc.

In the other sections you'll find: Running menus in both GameMode and MenuMode when your script is too large Ensuring your menus are seen Allowing for an infinite choice of a number Centralizing your menu exits