Game Development Community

dev|Pro Game Development Curriculum

How to get Map Type Filtering

by Kenneth Donnelly · 09/05/2005 (6:56 pm) · 4 comments

This little resource is how to get map types to be displayed into list.
For example, if you are making a Unreal tournament style game then you want map types to go with game modes.This is my first resource post and my mind works fast so bear with me.

Let's Start with something easy at first...actually making the maps have a type.

Open a mission file (*.mis) from your /mission folder.
You should see something like this:
//--- OBJECT WRITE BEGIN ---
new SimGroup(MissionGroup) {
      cdTrack = "2";
      musicTrack = "InGameMusic";
      CTF_scoreLimit = "5";

   new ScriptObject(MissionInfo) {
         name = "Stronghold";
         desc0 = "Starter.fps map";
         descLines = "2";
         
   };
   new MissionArea(MissionArea) {
      Area = "-384 -736 720 1296";
      flightCeiling = "300";
      flightCeilingRange = "20";
         locked = "true";
   };

add MapType = "DM"; into the missioninfo block so it looks like this
new ScriptObject(MissionInfo) {
         name = "Mage's Keep";
         desc0 = "The Mage is studying the effects of fire and you";
         descLines = "2";
   [b]  MapType = "DM"; [/b]
   };

Notice it says DM in the Maptype, I have just gave the map a DM map type. You can have it say anything for you maps obviously.

Next actually getting them to filter...

Open up startMissionGui.gui from your starter.fps/client/ui folder
You will notice 3 functions at the bottom,
1. function SM_StartMission()
2. function startMissionGui::onWake()
3. function getMissionDisplayName( %missionFile )

the first function says (in english) that the game will load up what ever map is listed on the selected line of the GuiTextListCtrl(SM_missionList) control, which has been created earlier in the file we are in.

the second function says (in enlish) that second that startMissionGui has been loaded it should do the following
1. clear the GuiTextListCtrl(SM_missionList) control's list
2. Find all the .mis files
3. Get the name of the map from the .mis file and load that name on the GuiTextListCtrl(SM_missionList) control list
4. Sort the list
5. Set the first one on the list as selected
6. Set the scroll bar visible

Step 3 is what we are interested in, it calls the 3rd function in the file function getMissionDisplayName( %missionFile ) and it actually reads the .mis file and returns with the value in the name varible
name = "Stronghold";

So since we already have a function that does exactally what we want copy and paste it below it in the file and we have to chage a few things to make it what we want

copy
//----------------------------------------
function getMissionDisplayName( %missionFile ) 
{
   %file = new FileObject();
   
   %MissionInfoObject = "";
   
   if ( %file.openForRead( %missionFile ) ) {
		%inInfoBlock = false;
		
		while ( !%file.isEOF() ) {
			%line = %file.readLine();
			%line = trim( %line );
			
			if( %line $= "new ScriptObject(MissionInfo) {" )
				%inInfoBlock = true;
			else if( %inInfoBlock && %line $= "};" ) {
			%inInfoBlock = false;
			%MissionInfoObject = %MissionInfoObject @ %line; 
				break;
			}
			
			if( %inInfoBlock )
		%MissionInfoObject = %MissionInfoObject @ %line @ " "; 	
		}
		
		%file.close();
	}
	%MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject;
	eval( %MissionInfoObject );
	
   %file.delete();

   if( %MissionInfoObject.name !$= "" )
      return %MissionInfoObject.name;
   else
      return fileBase(%missionFile); 
}

and move to the last line and press enter 2 for 2 spaces below it and then paste it .
Now make these changes:
function getMissionDisplay[b]Type[/b]( %missionFile )
{
   %file = new FileObject();

   %MissionInfoObject = "";

   if ( %file.openForRead( %missionFile ) ) {
		%inInfoBlock = false;

		while ( !%file.isEOF() ) {
			%line = %file.readLine();
			%line = trim( %line );

			if( %line $= "new ScriptObject(MissionInfo) {" )
				%inInfoBlock = true;
			else if( %inInfoBlock && %line $= "};" ) {
			%inInfoBlock = false;
			%MissionInfoObject = %MissionInfoObject @ %line;
				break;
			}

			if( %inInfoBlock )
	              %MissionInfoObject = %MissionInfoObject @ %line @ " ";
		}

		%file.close();
	}
	%MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject;
	eval( %MissionInfoObject );

   %file.delete();

   if( %MissionInfoObject.[b]MapType[/b] !$= "" )
      return %MissionInfoObject.[b]MapType[/b];
   else
      return fileBase(%missionFile);
}

ok now we have a function getting the map type we declared in the .mis file now we have to get it to display only these types in the GuiTextListCtrl(SM_missionList) control which is the box that list all the maps.
We have to go back to the 2nd function and make a few changes to get the function to only add DM map Types to the GuiTextListCtrl(SM_missionList) control list.

We accomplished this by making these changes:
function startMissionGui::onWake()
{
   SM_missionList.clear();
   %i = 0;
   for(%file = findFirstFile($Server::MissionFileSpec); %file !$= ""; %file = findNextFile($Server::MissionFileSpec))  
      if (strStr(%file, "/CVS/") == -1)
       [b] if (getMissionDisplayType(%file) $= "DM") [/b]
SM_missionList.addRow(%i++, getMissionDisplayName(%file) @ "\t" @ %file );
       [b]else[/b]
      [b]%i++;[/b]	
   
   SM_missionList.sort(0);
   SM_missionList.setSelectedRow(0);
   SM_missionList.scrollVisible(0);
}

What we did was: after it finds the .mis files it checks the MapType variable and if it has a DM value then it gets added to the list else it skips that .mis file without adding it to the list and moves to the next .mis file to check.

Now save all changes, delete the startMissionGui.gui.dso and start up torquedemo.exe and click start mission..., now only the .mis files with the MapType = "DM"; will be shown on the list to chose from.

Now I didn't go into great depth with this resource, I didn't point out how to make it so you can click a button and show DM maps and click another button for CTF maps because that would complicate things more, but if you want that functionality basically you just have to follow these steps:
1. add new button to main menu
2. change Start mission.... text to DM
3. Make text for the new button CTF or whatever map type you want,
4. Make the command for the new button: command = "Canvas.setContent(CTFstartMissionGui);";
5. Save the main menu gui and exit
6. Copy and paste startMissionGui.gui and rename it to CTFstartMissionGui.gui
6. Open it and make these changes:

Original
//--- OBJECT WRITE BEGIN ---
new GuiChunkedBitmapCtrl(startMissionGui) {
   profile = "GuiContentProfile";
   horizSizing = "width";
   vertSizing = "height";
   position = "0 0";
   extent = "640 480";
   minExtent = "8 8";
   visible = "1";
   bitmap = "./background";
   useVariable = "0";
   tile = "0";
      helpTag = "0";

   new GuiControl() {
      profile = "GuiWindowProfile";
      horizSizing = "center";
      vertSizing = "center";
      position = "92 86";
      extent = "455 308";
      minExtent = "8 8";
      visible = "1";
         helpTag = "0";

      new GuiTextCtrl() {
         profile = "GuiTextProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "12 36";
         extent = "72 18";
         minExtent = "8 8";
         visible = "1";
         text = "Select Mission:";
         maxLength = "255";
            helpTag = "0";
      };
      new GuiCheckBoxCtrl(ML_isMultiplayer) {
         profile = "GuiCheckBoxProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "155 272";
         extent = "147 23";
         minExtent = "8 8";
         visible = "1";
         variable = "pref::HostMultiPlayer";
         text = "Host Multiplayer";
         groupNum = "-1";
         buttonType = "ToggleButton";
            helpTag = "0";
            maxLength = "255";
      };
      new GuiButtonCtrl() {
         profile = "GuiButtonProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "320 271";
         extent = "127 23";
         minExtent = "8 8";
         visible = "1";
         command = "SM_StartMission();";
         text = "Launch Mission!";
         groupNum = "-1";
         buttonType = "PushButton";
            helpTag = "0";
      };
      new GuiScrollCtrl() {
         profile = "GuiScrollProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "10 62";
         extent = "436 200";
         minExtent = "8 8";
         visible = "1";
         willFirstRespond = "1";
         hScrollBar = "dynamic";
         vScrollBar = "alwaysOn";
         constantThumbHeight = "0";
         childMargin = "0 0";
            helpTag = "0";
            defaultLineHeight = "15";

         new GuiTextListCtrl(SM_missionList) {
            profile = "GuiTextArrayProfile";
            horizSizing = "right";
            vertSizing = "bottom";
            position = "2 2";
            extent = "414 16";
            minExtent = "8 8";
            visible = "1";
            enumerate = "0";
            resizeCell = "1";
            columns = "0";
            fitParentWidth = "1";
            clipColumnText = "0";
               helpTag = "0";
               noDuplicates = "false";
         };
      };
      new GuiTextEditCtrl() {
         profile = "GuiTextEditProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "98 15";
         extent = "134 18";
         minExtent = "8 8";
         visible = "1";
         variable = "pref::Player::Name";
         maxLength = "255";
         historySize = "0";
         password = "0";
         tabComplete = "0";
         sinkAllKeyEvents = "0";
            helpTag = "0";
      };
      new GuiTextCtrl() {
         profile = "GuiTextProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "12 11";
         extent = "63 18";
         minExtent = "8 8";
         visible = "1";
         text = "Player Name:";
         maxLength = "255";
            helpTag = "0";
      };
      new GuiButtonCtrl() {
         profile = "GuiButtonProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "10 272";
         extent = "127 23";
         minExtent = "8 8";
         visible = "1";
         command = "Canvas.setContent(mainMenuGui);";
         text = "<< Back";
         groupNum = "-1";
         buttonType = "PushButton";
            helpTag = "0";
      };
   };
};
//--- OBJECT WRITE END ---


//----------------------------------------
function SM_StartMission()
{
   %id = SM_missionList.getSelectedId();
   %mission = getField(SM_missionList.getRowTextById(%id), 1);

   if ($pref::HostMultiPlayer)
      %serverType = "MultiPlayer";
   else
      %serverType = "SinglePlayer";

   createServer(%serverType, %mission);
   %conn = new GameConnection(ServerConnection);
   RootGroup.add(ServerConnection);
   %conn.setConnectArgs($pref::Player::Name);
   %conn.setJoinPassword($Client::Password);
   %conn.connectLocal();
}


//----------------------------------------
function startMissionGui::onWake()
{
   SM_missionList.clear();
   %i = 0;
   for(%file = findFirstFile($Server::MissionFileSpec); %file !$= ""; %file = findNextFile($Server::MissionFileSpec))  
      if (strStr(%file, "/CVS/") == -1)
         if (getMissionDisplayType(%file) $= "DM")
         SM_missionList.addRow(%i++, getMissionDisplayName(%file) @ "\t" @ %file );
         else
         %i++;

   SM_missionList.sort(0);
   SM_missionList.setSelectedRow(0);
   SM_missionList.scrollVisible(0);
}   


//----------------------------------------
function getMissionDisplayName( %missionFile ) 
{
   %file = new FileObject();
   
   %MissionInfoObject = "";
   
   if ( %file.openForRead( %missionFile ) ) {
		%inInfoBlock = false;
		
		while ( !%file.isEOF() ) {
			%line = %file.readLine();
			%line = trim( %line );
			
			if( %line $= "new ScriptObject(MissionInfo) {" )
				%inInfoBlock = true;
			else if( %inInfoBlock && %line $= "};" ) {
		                   %inInfoBlock = false;
			%MissionInfoObject = %MissionInfoObject @ %line; 
				break;
			}
			
			if( %inInfoBlock )
	  %MissionInfoObject = %MissionInfoObject @ %line @ " "; 	
		}
		
		%file.close();
	}
	%MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject;
	eval( %MissionInfoObject );
	
   %file.delete();

   if( %MissionInfoObject.name !$= "" )
      return %MissionInfoObject.name;
   else
      return fileBase(%missionFile); 
}
{/code]

changes
[code]
//--- OBJECT WRITE BEGIN ---
new GuiChunkedBitmapCtrl([b]CTFstartMissionGui[/b]) {
   profile = "GuiContentProfile";
   horizSizing = "width";
   vertSizing = "height";
   position = "0 0";
   extent = "640 480";
   minExtent = "8 8";
   visible = "1";
   bitmap = "./background";
   useVariable = "0";
   tile = "0";
      helpTag = "0";

   new GuiControl() {
      profile = "GuiWindowProfile";
      horizSizing = "center";
      vertSizing = "center";
      position = "92 86";
      extent = "455 308";
      minExtent = "8 8";
      visible = "1";
         helpTag = "0";

      new GuiTextCtrl() {
         profile = "GuiTextProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "12 36";
         extent = "72 18";
         minExtent = "8 8";
         visible = "1";
         text = "Select Mission:";
         maxLength = "255";
            helpTag = "0";
      };
      new GuiCheckBoxCtrl(ML_isMultiplayer) {
         profile = "GuiCheckBoxProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "155 272";
         extent = "147 23";
         minExtent = "8 8";
         visible = "1";
         variable = "pref::HostMultiPlayer";
         text = "Host Multiplayer";
         groupNum = "-1";
         buttonType = "ToggleButton";
            helpTag = "0";
            maxLength = "255";
      };
      new GuiButtonCtrl() {
         profile = "GuiButtonProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "320 271";
         extent = "127 23";
         minExtent = "8 8";
         visible = "1";
         command = "SM_StartMission();";
         text = "Launch Mission!";
         groupNum = "-1";
         buttonType = "PushButton";
            helpTag = "0";
      };
      new GuiScrollCtrl() {
         profile = "GuiScrollProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "10 62";
         extent = "436 200";
         minExtent = "8 8";
         visible = "1";
         willFirstRespond = "1";
         hScrollBar = "dynamic";
         vScrollBar = "alwaysOn";
         constantThumbHeight = "0";
         childMargin = "0 0";
            helpTag = "0";
            defaultLineHeight = "15";

         new GuiTextListCtrl([b]CTFSM_missionList[/b]) {
            profile = "GuiTextArrayProfile";
            horizSizing = "right";
            vertSizing = "bottom";
            position = "2 2";
            extent = "414 16";
            minExtent = "8 8";
            visible = "1";
            enumerate = "0";
            resizeCell = "1";
            columns = "0";
            fitParentWidth = "1";
            clipColumnText = "0";
               helpTag = "0";
               noDuplicates = "false";
         };
      };
      new GuiTextEditCtrl() {
         profile = "GuiTextEditProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "98 15";
         extent = "134 18";
         minExtent = "8 8";
         visible = "1";
         variable = "pref::Player::Name";
         maxLength = "255";
         historySize = "0";
         password = "0";
         tabComplete = "0";
         sinkAllKeyEvents = "0";
            helpTag = "0";
      };
      new GuiTextCtrl() {
         profile = "GuiTextProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "12 11";
         extent = "63 18";
         minExtent = "8 8";
         visible = "1";
         text = "Player Name:";
         maxLength = "255";
            helpTag = "0";
      };
      new GuiButtonCtrl() {
         profile = "GuiButtonProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "10 272";
         extent = "127 23";
         minExtent = "8 8";
         visible = "1";
         command = "Canvas.setContent(mainMenuGui);";
         text = "<< Back";
         groupNum = "-1";
         buttonType = "PushButton";
            helpTag = "0";
      };
   };
};
//--- OBJECT WRITE END ---


//----------------------------------------
function [b]CTFSM_StartMission()[/b]
{
   %id = [b]CTFSM_missionList.getSelectedId();[/b]
   %mission = getField([b]CTFSM_missionList[/b].getRowTextById(%id), 1);

   if ($pref::HostMultiPlayer)
      %serverType = "MultiPlayer";
   else
      %serverType = "SinglePlayer";

   createServer(%serverType, %mission);
   %conn = new GameConnection(ServerConnection);
   RootGroup.add(ServerConnection);
   %conn.setConnectArgs($pref::Player::Name);
   %conn.setJoinPassword($Client::Password);
   %conn.connectLocal();
}


//----------------------------------------
function [b]CTFstartMissionGui[/b]::onWake()
{
   [b]CTFSM_missionList[/b].clear();
   %i = 0;
   for(%file = findFirstFile($Server::MissionFileSpec); %file !$= ""; %file = findNextFile($Server::MissionFileSpec))  
      if (strStr(%file, "/CVS/") == -1)
         if (getMissionDisplayType(%file) $= "[b]CTF[/b]")
             [b]CTFSM_missionList[/b].addRow(%i++, getMissionDisplayName(%file) @ "\t" @ %file );
         else
         %i++;

   [b]CTFSM_missionList[/b].sort(0);
   [b]CTFSM_missionList[/b].setSelectedRow(0);
   [b]CTFSM_missionList[/b].scrollVisible(0);
}   


//----------------------------------------
function getMissionDisplayName( %missionFile ) 
{
   %file = new FileObject();
   
   %MissionInfoObject = "";
   
   if ( %file.openForRead( %missionFile ) ) {
		%inInfoBlock = false;
		
		while ( !%file.isEOF() ) {
			%line = %file.readLine();
			%line = trim( %line );
			
			if( %line $= "new ScriptObject(MissionInfo) {" )
				%inInfoBlock = true;
			else if( %inInfoBlock && %line $= "};" ) {
			%inInfoBlock = false;
			%MissionInfoObject = %MissionInfoObject @ %line; 
				break;
			}
			
			if( %inInfoBlock )
	%MissionInfoObject = %MissionInfoObject @ %line @ " "; 	
		}
		
		%file.close();
	}
	%MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject;
	eval( %MissionInfoObject );
	
   %file.delete();

   if( %MissionInfoObject.name !$= "" )
      return %MissionInfoObject.name;
   else
      return fileBase(%missionFile); 
}

Than save as CTFstartMissionGui.gui
startup torquedemo.exe and the DM button loads up the DM maps and the new button you created loads up CTF maps, if you want ti to load up something else just change the one line in onWake(); function:
if (getMissionDisplayType(%file) $= "CTF")

I wrote this at 3am so I hope its bug free, let me know if its not and I will make changes.

#1
09/06/2005 (10:15 am)
One thing that just occurred to me while reading this..

It might be better to return the whole missioninfo object from the file reader, rather than retrieving the name or game type. That way the loop logic can interrogate whatever it likes..

so instead of getMissionDisplayName you would have getMissionInfo() which would return the missioninfo object (you would pass the missioninfo object variable in so that it doesnt go out of scope), then fill in that missioninfo object with the mission info. Then check it with isObject or somethign when its been parsed.

That way, you could do multiple tests for validity (like name = blah + gametype = blah + map = blah)

See what I'm getting at? having two functions which duplicate the functionality only to return different members of a single object seems worse than just returning the object.

Just my two pence.
#2
09/13/2005 (6:30 am)
Good point, Phil. I 've got the same idea :)

Anyway, Kenneth, you did a good job!
#3
09/14/2005 (4:24 pm)
Thanks I just wanted to use stock code for the newbies, It seems to me there is a gap between beginners and intermediate. I wrote this guide with simplicity in mind.
#4
12/04/2005 (5:37 pm)
You can condense a very large portion of that code into this one line...


eval( %MissionInfoObject.MapType @ "SM_StartMission()" );


If "MapType" is not defined, it will default to the original function automatically.