GUI Scripting: Mission Start Example
This page is part of a series. See GUI Scripting Language for overview.
A Simple Popup Message, Invoked by the TDM System
If you are new to GUI scripting, this example shows how the essential GUI elements are composed. You might design a similar composition if coding a custom GUI for your FM. But keep in mind, this example is actually part of the main menu system, so the popup's creation and dismissal reflects that.
To paraphrase greebo, after the mission is loaded and before the actual gameplay, a popup message is shown to the player. The English-language version of this reads:
Mission Loaded Press 'Attack' to start the mission.
The GUI that implements that is shown here, but slightly simplified and with 2 files (tdm_waituntilready.gui and tdm_waituntilready_custom.gui) merged. This GUI is discussed in the Commentary, further below.
windowDef WaitUntilReady { rect 0,0,640,480 visible 1 nocursor 1 windowDef Parchment { rect 190,140,260,180 nocursor 1 background "guis/assets/mainmenu/oldparchment_backdrop3" matcolor 0,0,0,0 windowDef Title { rect 10,30,240,50 visible 1 text "#str_02446" // Mission loaded forecolor 0,0,0,0 textscale 0.24 textalign 1 font "fonts/carleton" } windowDef Message { rect 10,85,240,100 visible 1 text "#str_02447" // Press Attack to start the Mission forecolor 0,0,0,0 textscale 0.22 textalign 1 font "fonts/carleton" } } // Called right after startup, to do fade-in animation. windowDef OpenWindowAnimation { notime 1 onTime 0 { // Fade in the message box transition "Parchment::matcolor" "0 0 0 0" "1 1 1 1" "500"; transition "Message::forecolor" "0 0 0 0" "0 0 0 1" "500"; transition "Title::forecolor" "0 0 0 0" "0 0 0 1" "500"; } } // Called when the player clicks somewhere in the GUI, to do fade-out. windowDef CloseWindowAnimation { notime 1 onTime 0 { // Start the fade out of this messagebox transition "Parchment::matcolor" "1 1 1 1" "1 1 1 0" "500"; transition "Message::forecolor" "0 0 0 1" "0 0 0 0" "500"; transition "Title::forecolor" "0 0 0 1" "0 0 0 0" "500"; } } onTime 0 { // Call the openwindow routine set "OpenWindowAnimation::notime" "0"; resetTime "OpenWindowAnimation" 0; } onAction { // Tell the SDK to destroy this overlay in 1200 msecs set "gui::DestroyDelay" 1200; // Issue the playerIsReady command to start regular gameplay set "cmd" "playerIsReady"; // Call the closewindow routine set "CloseWindowAnimation::notime" "0"; resetTime "CloseWindowAnimation" 0; } }
Commentary for Beginners to GUI Scripting
The overall windowDef, here named "WaitUntilReady", has 3 properties. The first, "rect", establishes a full-size overlay on the game screen, with the standard 640w x 480h coordinate system. The mouse cursor is suppressed. While visibility is true, the overlay has neither matcolor (material or texture color) nor backcolor (solid color) defined; the default of the latter, transparent in alpha, takes effect. As will be seen, this gui doesn't toggle the visibility, but rather does fade-in/fade-out with the alpha channel. (The term "animation" is used loosely here to refer to these fades.)
Next, the location of child windowDef "Parchment" and grandchildren "Title" and "Message" are defined. X,Y upper left corner of each is relative to its parent. The parchment background comes from a .dds texture, but matcolor alpha channel is 0, so it's initially invisible, as are its children's backgrounds. Their font's "forecolor", while black, also are initialized with alpha 0, so transparent.
The next two windowDefs, "OpenWindowAnimation" and "CloseWindowAnimation", are snoozing. Their timers are not running because their "notime" properties are initialized as true.
This is not the case with the top-level WaitUntilReady. So, when WaitUntilReady is instantiated, its timer starts running and immediately calls it's "onTime 0" event handler. The first thing that does is start the timer belonging to "OpenWindowAnimation" (and just to be safe, resets that timer to 0, which also starts the timer). As a result, inside OpenWindowAnimation, its own "onTime 0" handler immediately runs. That begins the fade in process, changing alphas from 0 to 1 over the course of 500 milliseconds.
Later, when the user hits the left mouse button (anywhere, because there is no cursor shown), the "onAction" event handler reverses that process, doing a fade-out. But first, it sets GUI parameter gui::StartDelay known to the C++ code, and issues the "playerIsReady" command to the C++ system, using the method of GUI Scripting: Parsing of Set 'Cmd'. If you were making your own GUI that interfaced to an FM-specific SCRIPT file, you might use a gui:: parameter, but not the Set 'Cmd' method.
FYI. Interfacing this GUI to the C++ Code
This relates to C++ engine coding, so feel free to skip it.
The heart of interfacing is in Player.cpp's idPlayer::WaitUntilReady() function, which, once the game loads, gets called every frame. Some highlights within that function are shown here.
When first called, create the GUI overlay...
m_WaitUntilReadyGuiHandle = CreateOverlay(cv_tdm_waituntilready_gui_file.GetString(), LAYER_WAITUNTILREADY);
...and the internal user-interface data object:
idUserInterface* gui = GetOverlay(m_WaitUntilReadyGuiHandle);
When the system detects a change in the "attack" mouse button state, that "gui" object pointer is used to create an event for it to catch in the onAction event handler. After that handling gets to...
set "cmd" "playerIsReady"; // in .gui code
...the WaitUntilReady function code continues with:
idStr cmd = gui->HandleEvent(...); if (cmd == "playerIsReady") { int delay = gui->GetStateInt("DestroyDelay"); PostEventMS(&EV_DestroyOverlay, delay, m_WaitUntilReadyGuiHandle); ... }