How to add a construction objective

The information on this page is specific to Enemy Territory: Quake Wars .


This tutorial should let you have a bare bones map with a GDF construction objective. The objective will be to construct a wall. Completing it will end the map.

Important Notes:

  • Since everyone’s setup is different <savepath> means the path that you give the SDK launcher.
  • Replace <yourmap> where it’s mentioned below with the name of the map you’re making. Think of a single word like “valley”, “quarry”, etc. CAPITALISATION MATTERS!

Setup

Firstly, create the following folders:

  • <savepath>/base/mapinfo
  • <savepath>/base/script/maps/<yourmap>
  • <savepath>/base/maps/<yourmap>

Secondly, create the following files (you can use Notepad but make sure you set the file extension correctly):

  • <savepath>/base/pakmeta.conf
  • <savepath>/base/mapinfo/<yourmap>.md
  • <savepath>/base/script/maps/<yourmap>.script

Files

In the pakmeta.conf you need to paste the following:

// Map Data

mapMetaData maps/<yourmap>/<yourmap> {
    "pretty_name"           "<whateveryourwant>"
    "mapinfo"               "<yourmap>"
    "massive_zone_name"     "<yourmap>"
    "server_shot_thumb"     "levelshots/thumbs/area22.tga"
    "show_in_browser"       "1" 
}

In the <yourmap>.md file paste the following:

#include <mapinfo/mapinfo.include>

mapInfoDef <yourmap> {
    _default_mapinfo( "<yourmap>" )

    data {
        "mapLocation"           "maps/<yourmap>/location"
        "script_entrypoint"     "<yourmap>_MapScript"
        "mapBriefing"           "maps/<yourmap>/briefing"
        "campaignDescription"   "maps/<yourmap>/campaign"
        "numObjectives"         "4"
        "mtr_serverShot"        "levelshots/<yourmap>"
        "mtr_backdrop"          "levelshots/campaigns/northamerica"
        "mapPosition"           "311 140"
        "snd_music"             "sounds/music/load1"
        "strogg_endgame_pause"  "6.0"
        "gdf_endgame_pause"     "5.0"
    }
}

Create a text file called ’ map.script in your <savepath>/base/script/ location. Edit it, and add the following line:

#include "script/maps/<yourmap>.script"

The map

Okay, time to build the map. Start the SDK Launcher, and set editWorld to begin. Create an empty caulk box as shown in “|this tutorial. I suggest a size of 2048 x 2048 x 2048. Press Esc to deselect everything.

Now repeat it with a box with a size of 512 x 512 x 512 and add an atmosphere as shown [1] . Basically, you’ll have two areas in the map, the outside which is the big box and the inside which is the smaller box. The box face which you applied the “outside_portal” texture acts as a gateway between the two areas. You’ll now have visible explosions, etc. Press Esc to deselect everything.

Now place an “info_team_spawn_gdf” somewhere inside the big box. This is where the GDF will spawn. Now let’s add a simple construct objective for the GDF to build. Press Esc to deselect everything.

Firstly add a “constructible_materials_gdf” entity onto your big box somewhere. With it selected, press N which will take you into the Properties Editor. Change the name to “construct_materials” and add the following key/value pairs:

constructed   wall
construction   wall_temp
objective_index   0
object_name   maps/generic/bridge
task_name   maps/valley/task_bridge

You can also add sounds text and whatnot but for this bare-bones map this will be sufficient. Press Esc to deselect everything.

Now add another brush, say a 256 x 16 x 128 wall-like brush. With it selected, right click and select “constructible > constructible_base_etqwmap” and press N. Change its name to “wall” so that will be referenced by the constructions materials entity we edited previously. Press Esc to deselect everything.

Repeat the above but now name it “wall_temp”.

In the Properties Editor select the “worldspawn” entity and add the following key/value pair:

script_construct_entity   construct_materials

Now save the map to <savepath>/base/maps/<yourmap>/ <yourmap>.world and then compile it (Compile > Normal).

The script

Let’s now edit our <savepath>/base/script/maps/ <yourmap>.script file we had created at the start. I’m going to break this into sections so they’re easier to follow but if you’re not really that interested in the why just paste everything in order and find/replace <yourmap> there.

#define OBJECTIVE_<yourmap_inCAPS>_BUILD_WALL  0

This will define the first objective (numbered 0) and assign it a name so it can be easily retrieved without having to know whether its the first or last objective. In our case we only have one objective so this is mainly for future maps.

object mapObject_<yourmap> : mapObject_Default {
    void        InitObjectives();
    void        CompleteObjective( float index, entity p );
    void        StartFirstObjective();
    void        OnWallBuilt();
    void        OnTimeLimitHit();

    handle  GetObjectiveMessage( float index );

    float       mainObjectiveIndex;
    entity  wallConstruction;
}

This piece of code lets the game know that your map has a couple of properties and custom methods that you’re going to be using. Of interest here are the “OnWallBuilt()” and wallConstruction. The first is an “event” which will be fired once the construction objective (our wall) is built. The later is a temporary entity that will reference our map construction entity.

mapObject_Base <yourmap>_MapScript() {
    return new mapObject_<yourmap>;
}

This block is used internally to generate a new map script.

void mapObject_<yourmap>::InitObjectives() {
    gameRules.setWinningTeam( stroggTeam );
    gdfTeam.SetRespawnWait( 20 );
    stroggTeam.SetRespawnWait( 20 );
    CreateRespawnTimeThread( gdfTeam );

    wallConstruction = worldspawn.getEntityKey( "script_construct_entity" );

    mainObjectiveIndex = OBJECTIVE_<yourmap_inCAPS>_BUILD_WALL;

    thread  StartFirstObjective();

    objManager.setNextObjective( gdfTeam, mainObjectiveIndex );
    objManager.setNextObjective( stroggTeam, mainObjectiveIndex );
}

This piece of code initialises the objective system. The first four lines tell the game which team is defending, and sets up the spawn timers. Now this is important: the “wallConstruction” variable that we declared right at the top of the script will be assigned the value that you gave the key/value pair of the worldspawn. So when the game runs “wallConstruction” will become the same as the “constructible_materials_gdf” entity we placed on the map.

Then the script fetches the objective number and sets it as the next objective for both teams. Meanwhile it also starts the first objective. Which is what the next block shows:

void mapObject_<yourmap>::StartFirstObjective() {
    sys.wait( 5.f );

    objManager.SetObjectiveEntity( wallConstruction, mainObjectiveIndex );

    CreateInitialTimedMission( wallConstruction );
    wallConstruction.vCreateMission();
    wallConstruction.vStartObjective();
}

Firstly the game will wait a bit and then set our wallConstruction entity as the first objective and say it has an index of 0. It will create the first mission (those missions on the top left of the screen), etc.

void mapObject_<yourmap>::CompleteObjective( float index, entity p ) {
    if ( index == OBJECTIVE_<yourmap_inCAPS>_BUILD_WALL ) {
        OnWallBuilt();
    }
}

handle mapObject_<yourmap>::GetObjectiveMessage( float index ) {
    if ( index == OBJECTIVE_<yourmap_inCAPS>_BUILD_WALL ) {
        return sys.localizeString( "maps/valley/obj_bridge" );
    }

    return g_locStr_BadObjective;
}

The first block will fire off the “OnWallBuilt” event we declared earlier, the second is just for printing some text so don’t worry about that one.

void mapObject_<yourmap>::OnWallBuilt() {
    StopTimedMission();
    wallConstruction.vCompleteMission();

    wallConstruction.vFinishObjective();

    gameRules.setWinningTeam( gdfTeam );
    gameRules.endGame();

    wallConstruction.vCompleteMission();
}

Okay, this is the event that’s fired when the wall is constructed. This is our only objective so after this we complete missions, objectives, set which team won the map and end the game. But what if the GDF can’t construct this in the time limit?

void mapObject_<yourmap>::OnTimeLimitHit() {
    FinishTimedMission();
    objManager.SetObjectiveEntity( $null_entity, -1 );

    gameRules.endGame();
}

Since in this map the Strogg are defending they automatically win so no need to set the winning team (as we had already set it near the top). The other lines simply clean up the objectives a bit (setting them to nothing).

With the SDK Launcher , start the game, bring down the console and type: ” devmap <yourmap>/<yourmap>” and press Enter.

In certain situations, the game will not load the map but print out the following error:

spawn map denied: unknown campaign 'yourmap'

In that case, type “seta si_rules sdGameRulesObjective” and press <Enter> before trying devmap again. This will switch the game type from campaign mode to objective mode.