GUI Scripting: In-World Menu Examples: Difference between revisions
create this article |
m →Setting Up the Entity: drop mention of gui_noninteractive |
||
Line 21: | Line 21: | ||
gui guis/testchooser.gui | gui guis/testchooser.gui | ||
gui_parm1 0 | gui_parm1 0 | ||
The "Firepit", "Spiders", and "Fumes" implementations differ in their .script and testchooser.gui contents. | The "Firepit", "Spiders", and "Fumes" implementations differ in their .script and testchooser.gui contents. |
Revision as of 19:36, 22 August 2022
This is part of a series, GUI Scripting Language
Introduction
It is possible to offer the player a choice of some option using a menu on in-world surface, i.e., a menu that is not full-screen. Here, we look at a wall-plaque choice picker.
The behavior for this choice picker will be modeled on the standard "choiceDef", used widely in TDM's Settings main menu. However, the behavior has to be somewhat modified, due to limitations with a gui-on-surface, discussed more fully in GUI Scripting: Interactions. In particular:
- No cursor is shown
- Frobbing (typically RMB) is used for choice selection instead of Action (typically LMB).
- Frob highlighting takes the place of mouse-over text glowing.
- As with a choiceDef, cycling through available choices is done by repeated clicking. However, choiceDef offers additional selection methods, involving keystrokes, beyond what can be done here.
Three alternative implementations are shown, called "Firepit", "Spiders", and "Fumes".
Setting Up the Entity
All three examples will use the same entity, a stepped wall plaque composed from 2 rectangular brushes, combined into a func_static. See the "Adding Readables to your Map - from Scratch, Made from Brushes" section of Readables for how to do this, including texturing. For the specific entity here, use these dimensions and attributes:
- Inner and frontmost rectangle is 40w x 6h, with "Nodraw Solid" texture, except blue "Entity GUI" applied and fitted to front surface. This surface will show the menu text. We'll use black text, but any color could be specified. (You can substitute a patch if you prefer.)
- Outer and rearmost rectangle is 42w x 8h. Its front surface will provide the background for the control. To provide contrast with the black menu text, a light color texture works (here, textures/darkmod/fabric/shadowable/cloth_plain_creased_white_dull ). This rectangle is needed for frob highlighting to work well.
Once you've combined them into an entity, add these spawnargs:
frob_action_script change_choice frobable 1 gui guis/testchooser.gui gui_parm1 0
The "Firepit", "Spiders", and "Fumes" implementations differ in their .script and testchooser.gui contents.
Firepit – Emulating a ChoiceDef with a WindowDef
The active surface will be subdivided into left and right windowDef cells of equal size. The left will get the "Firepit" prompt, left-justified. The right will be where the various choices cycle, right-justified: "Simmer", "Bake", "Broil", and "Cremate". Those words will be provided by the global script function "change_choice", called on each frob, and passed to the testchooser.gui by GUI::Parameter choice_text. The GUI takes care of initializing this, in an onTime handler, while the script initializes the corresponding index.
The "change_choice" .script function
This is contained in any convenient .script file:
float picked = 0; // global init. void change_choice(entity me) { //sys.println ("change_choice() called"); picked++; if(picked > 3) picked = 0; // DO HERE: Change game world to match string s; if(picked == 0) s = "Simmer"; else if (picked == 1) s = "Bake"; else if (picked == 2) s = "Broil"; else s = "Cremate"; me.setGuiString(1, "choice_text", s);
};
The GUI
Our guis/testchooser.gui is:
#define ROW_START_Y 12 #define CONTENTS_WIDTH 640 #define CONTENTS_HEIGHT 96 #define MY_SCALE forceaspectwidth CONTENTS_WIDTH forceaspectheight CONTENTS_HEIGHT // Same ratio as blue Entity GUI surface of 40w x 6h // Background is rendered on separate surface windowDef Desktop { rect 0, 0, 640, 480 backcolor 0, 0, 0, 0 nocursor 1 MY_SCALE windowDef Prompt { rect 0,ROW_START_Y,320,480 textscale 0.9 textalign 0 // left justified forecolor 0, 0, 0, .85 font "fonts/carleton_condensed" text "Firepit" visible 1 } windowDef Which { rect 320,ROW_START_Y,320,480 textscale 0.9 textalign 2 // right justified textalignx -10 forecolor 0, 0, 0, .85 font "fonts/carleton_condensed" text "gui::choice_text" visible 1 } onTime 0 { set "gui::choice_text" "Simmer"; // same as picked == 0 in script } } ...
Spiders – Driving a ChoiceDef from a Script
In this version, instead of emulating a choiceDef, we actually use it, although driven indirectly through the script function. That function, instead of passing the string to the GUI, passes the numeric index that a choiceDef knows about. Since the "picked_index" is initialized in the GUI, it's unnecessary to do so in the SCRIPT file. See also GUI Scripting: ChoiceDef.
For Spiders, the example has a different set of choices than Firepit, repurposing an i18n string that's already provided in the standard distribution for AIVision settings: "Nearly Blind;Forgiving;Challenging;Hardcore".
The "change_choice" .script function
void change_choice(entity me) { //sys.println ("change_choice() called"); float i = me.getGuiInt(1, "picked_index"); i++; if(i > 3) i = 0; me.setGuiInt(1, "picked_index", i); };
The GUI
In this version of testchooser.gui, the initial #defines and the Desktop and Prompt windowDefs are the same (except for Prompt, the "text" reads "Spiders" instead of "Firepit"). Here's what's changed:
... choiceDef Which { rect 320,ROW_START_Y,320,480 MY_SCALE choices "#str_07323" // Nearly Blind;Forgiving;Challenging;Hardcore values "0;1;2;3" textscale 0.9 textalign 2 // right justified textalignx -10 forecolor 0, 0, 0, .85 font "fonts/carleton_condensed" gui "picked_index" // this is driven from script, responding to frob choiceType 0 visible 1 } onTime 0 { set "gui::picked_index" 0; } ...
In the choiceDef, MY_SCALE had to be added to prevent font smear. (Evidently, it differs from windowDef children, where the "forceaspect…" properties are expressed in a parent.)
Fumes – Adding Choice Transitions
To allow the choice text to change with a fade in/fade out, start with the Firepit GUI example, and clone the righthand windowDef, creating a separate one for each of the 4 possible hardcoded text strings. They'll all be formally visible, but the alpha of forecolor is 0 (so hidden) unless transitioned to 0.85. On the script side, a picked_index is passed to the GUI, as in the Spiders example, but with an event added to drive the transitions.
The "change_choice" .script function
The Spiders script function is just like Firepit's but with this line at the end:
me.callGui(1,"UpdateChoice");
The GUI
In this version of testchooser.gui, more #defines are added, so that the transitions and cloned windowDefs can be succinct and uniform:
#define NO_TEXT 0,0,0,0 #define SNO_TEXT "0 0 0 0" #define SNORMAL_TEXT "0 0 0 0.85" #define FADE_TIME 200 // milliseconds #define WHICH_BASE rect 320,ROW_START_Y,320,480 textscale 0.9 textalign 2 textalignx -10 font "fonts/carleton_condensed" visible 1 forecolor NO_TEXT
The Desktop and Prompt windowDefs are the same (except Prompt's "text" is "Fumes"). Then the latter part of the .gui becomes:
windowDef Which0 { WHICH_BASE text "Whiff" } windowDef Which1 { WHICH_BASE text "Haze" } windowDef Which2 { WHICH_BASE text "Stinging" } windowDef Which3 { WHICH_BASE text "Fatal" } onNamedEvent UpdateChoice { if("gui::picked_index" == 0) { transition "Which3::forecolor" SNORMAL_TEXT SNO_TEXT FADE_TIME; transition "Which0::forecolor" SNO_TEXT SNORMAL_TEXT FADE_TIME; } else if ("gui::picked_index" == 1) { transition "Which0::forecolor" SNORMAL_TEXT SNO_TEXT FADE_TIME; transition "Which1::forecolor" SNO_TEXT SNORMAL_TEXT FADE_TIME; } else if ("gui::picked_index" == 2) { transition "Which1::forecolor" SNORMAL_TEXT SNO_TEXT FADE_TIME; transition "Which2::forecolor" SNO_TEXT SNORMAL_TEXT FADE_TIME; } else { // "gui::picked_index" == 3 transition "Which2::forecolor" SNORMAL_TEXT SNO_TEXT FADE_TIME; transition "Which3::forecolor" SNO_TEXT SNORMAL_TEXT FADE_TIME; } } onTime 0 { set "gui::picked_index" 0; set "Which0::forecolor" SNORMAL_TEXT ; } ...
Other Ideas
Add Magic
You could enhance the script to trigger a puff of smoke or some sparks in front of the choice when it changes.
Multirow Menu - Separate Items
If you wanted to have a multirow menu, where each row provided choices for a different option (like the Settings menus), you could build it from separately-frobbable 1-row entities as above, stacked like wall tiles. Names of things would have to change to differentiate them, and frob-related attributes tweaked to avoid spatial conflicts; see Frobbing.
Multirow Menus - Single Item
With just one entity, it might be possible to emulate or repurpose a listDef, but where each frob click changed the row, and the selected row was indicated by an icon or color swatch.
Bomb Timer
Frob on the entity, and it starts showing seconds being counted down. Further frobbing does nothing. This involves an endless loop in the script file, something a script object is ideal for.
Price of Gold
Imagine that the price of gold nuggets varied during the game, either randomly or in response to what has happened so far. You could have a display show it, using a polling script object to invent the current value. This display might not require any frobbing. (A non-frobbable entity wouldn't require a stepped entity... a nodraw rectangle with an "GUI Entity" would be enough.)
Cycling Images Instead of Text
Each frob could cycle through background images instead of foreground text. If you needed to scale them, likely matscalex and matscaley will help.