GUI Scripting: Mission Start Example

From The DarkMod Wiki
Jump to navigationJump to search

This page is part of a series. See GUI Scripting Language for overview.

Introduction

If you are new to GUI scripting, this example shows:

  • how the essential GUI elements are composed.
  • the use of transitions and timers for visual effects.
  • how a windowDef that itself contains no visual aspects can be used to drive visual changes in other windowDefs.

You might design a similar GUI 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. For a non-main-menu custom GUI, you'd do creation and dismissal with a script object; see GUI Scripting: Popup Message Example.

A Simple Popup Message, Invoked by the TDM System

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);
	...
}