Making a Mission

This document is about the implementation details of making a mission for Bridge Commander. You should probably already have a good idea of what you want your mission to be like, and it also wouldn't hurt to take a glance at some of the mission scripts in the game (E2M6 is recommended as a fairly straightforward example). It is also recommended that you read the basics page as well as the API basics page before beginning.

There are also some sample custom missions, which are very straightforward, located in the Scripts/Custom/Tutorial directory in the SDK. It is strongly advised that you look them over, starting with the first one (which is very simple). You can access these missions from the Test Only menu when you run the game with the -TestMode parameter, under Custom Missions.

Missions are composed of a lot of Python functions. Some of them are called by the game, others you set up to be called by the mission itself.


Initialization and Termination

Each mission is required to have two functions, called Initialize(), and Terminate(). The C++ object that represents the mission is passed in. These two functions are the barest minimum necessary to constitute a "mission" -- you'll want lots more, to do mission-specific things, but this is the starting point for building a mission.

def Initialize(pMission):
	# In Initialize(), you set up everything you need for the mission.

	# Create the regions (space sets) for this mission
	CreateRegions()
	
	# Create bridge sets we need.
	CreateSets()
	
	# Create the starting objects.
	CreateStartingObjects(pMission)
	
	# Setup more mission-specific events.
	SetupEventHandlers()

def Terminate(pMission):
	# Do whatever shutdown stuff might be necessary.
	.
	.
	.
	return
The CreateRegions(), CreateSets(), CreateStartingObjects(), and SetupEventHandlers() functions are user-created functions that set up various elements of the mission. These "kick off" the mission.


Creating sets

Sets contain game objects, and you must have some in order to have any sort of mission. You'll typically create some space sets, where ships will travel and fight, and then some other sets in which you'll place animated characters.

Creating space sets is fairly easy, if you use the pre-existing sets that come with the game. You can just use the MissionLib function SetupSpaceSet() with the package name of the set. If you have a placement file for this set (for this mission), you can then load it afterwards.

# Creates and sets up the space set for Biranu 1.
pBiranu1Set = MissionLib.SetupSpaceSet("Systems.Biranu.Biranu1")

# Load the mission-specific placement file for this set.
import E2M6_Biranu1_P
E2M6_Biranu1_P.LoadPlacements(pBiranu1Set.GetName())
You can create bridge sets (for animated characters) easily, also, using another MissionLib function, SetupBridgeSet(). You specify the set name (used to refer to the set in the set manager), and the model to use, along with a camera position for the set.
	pStarbaseSet	= MissionLib.SetupBridgeSet("StarbaseSet", "data/Models/Sets/StarbaseControl/starbasecontrolRM.nif", -30, 65, -1.55)


Creating Objects

Once you've created some sets, you can populate them with objects. For example, you can create the player's ship, some other ships, and characters on other bridge sets. The player's bridge is set up specially, though, so you don't really need to do much to make it.

Creating the player

To create the player's ship, use the MissionLib function CreatePlayerShip().

MissionLib.CreatePlayerShip(sShipClass, pSet, pcName, sWaypoint, bUnloadShip)

Creating other ships

To create other ships, use the function in the loadspacehelper module, called CreateShip().

pGalor1 = loadspacehelper.CreateShip("Galor", pBiranu1Set, "Galor 1", "Galor1Start")

You may want to look at the AI editor documentation for information on how to create and assign AIs to ships.


Fleshing Out the Mission

Once you've created some objects and sets, and started the game, you're only partway to having created a fun mission. You want the game to react to things that happen during the mission, such as ships getting destroyed, the player hailing a ship, or the player entering a new area. You do this by handling events, setting timers, creating sequences of actions, setting up conditions, or using proximities (as mentioned in the basic concepts document).

Events can be sent by one object to another, and consist of a type (the event type), source object, destination object, and perhaps some auxiliary data, depending on the class of event. Events are ignored unless the destination object has a handler for that event type -- a lot of objects have handlers for events that are attached automatically, as part of their creation process. You can also create a broadcast handler for an event type, which will be called any time an event of that type is sent. There are more intricacies than what is presented here, but that's the basic idea.

As an example, many missions have broadcast handlers for the ET_ENTERED_SET and ET_EXITED_SET events. These are sent when an object enters, or exits, a set, naturally -- the event's destination is the object which is entering or exiting, so the handler can take special action based on which ship it is. For example, a mission may have a handler for ET_ENTERED_SET which checks when the player enters a certain set. When the player enters a certain set, a dialogue sequence plays. Look at the M3Gameflow example mission for some usage of event handlers.

Timers are objects that will send a specified event after a certain time has passed. It can optionally be a repeating timer, as well. Look at the MissionLib.CreateTimer() function for an easy-to-use interface for timers.

Sequences are used constantly in the game. Every chain of dialogue in the game is a sequence -- a lot of mission gameflow is done using sequences also. Sequences are basically more sophisticated ways of using timers -- you can set up a string of actions that will happen when the sequence is played. For example, you can set up a sequence to play three voice lines, one after the other, using character actions -- when the first action in the sequence is completed (the line has been said), the next will start. You can also specify delays between actions -- for example, you could have a script action that makes a ship explode occur 5 seconds after a dialogue line is done playing.

Probably the most important type of action in terms of mission scripting is the TGScriptAction. A script action calls a Python function (one you write, or a predefined one), with any number of parameters. You simply specify the module of the function (i.e. "MissionLib", "Maelstrom.Episode1.E1M1"), and the function name, and pass in whatever parameters you want. When the script action triggers, that function will be run with your parameters. This is an extremely powerful system for doing flexible missions, and also lends itself well to script reuse.

As with timers, the M3Gameflow mission is a good example of basic sequence creation and usage. You can also look at M4Complex for more examples.

Conditions and proximities are more advanced mission building features, and will not be covered here. Look at the missions that ship with the game for usage (and in the case of proximities, look at MissionLib.ProximityCheck).


Putting a mission in the Custom Missions menu

The Custom Missions menu appears in the Test Only menu, when the game is started with the -TestMode command-line parameter. You can set up your missions so that they automatically place a button or menu for themselves into this menu.


Created on ... February 04, 2002