news about forums links tools tutorials contact

Capture the Flag - author: crack_addict
Note: This was made for Unreal, before UT was released and came with Capture the Flag builtin, so its now kind of obsolete, but I've retained it simply because its a good exercise in making a new gametype. This tutorial covers 3 basic classes used to make a Capture the Flag game, CTFGame, FlagSpawnPoint, and Flag. CTFGame is a GameInfo that handles the startup of games and is a subclass of TeamGame. FlagSpawnPoint is a NavigationPoint that handles the creation of the Flags in a level. Flag is a Pickup that is the flag(s), and it takes care of checking for captures...etc.
This tutorial covers how to make a *basic* Capture the Flag game with very basic scoring and other rules. Feel free to use this as a base to add CTF support to your modification, and if you make money off of it, at least think kindly of me if you're too stingy to send me the money. :)

    //=====================================================
    // CTFGame - Capture the Flag GameInfo
    //=====================================================

Since Unreal already has a Team game setup, and CTF requires teams, we only need to subclass the TeamGame class in order to get team support in. In this class we need to make sure the flags get spawned, and handle scoring for teams/individuals.

    class CTFGame expands TeamGame;

The number of points to award to the player and team upon a successful capture.

    var() int CaptureScore;

In PostBeginPlay() we search for all FlagSpawnPoints and tell them to spawn a flag. NOTE: This requires that maps need to have FlagSpawnPoints already placed in them in order to properly spawn flags (although you could easily write some code to play FlagSpawnPoints if none are present).

    function PostBeginPlay()
    {
    clocal FlagSpawnPoint fs;

      foreach AllActors(class'FlagSpawnPoint', fs)
      {
        fs.SpawnFlag();
      }
    }

ScoreTeamCapture does exactly what the name implies, it scores points for a capture.

    function ScoreTeamCapture(byte Team, PlayerPawn Scorer)
    {

TeamInfo is a class (under the Info class) that is used to store pertinent information about different teams in a Team game. We need to use it to add points to the entire team, as well as the individual capturer.

      local TeamInfo ti;

Find a matching TeamInfo based on Team and then give them some points.

      foreach AllActors(class'TeamInfo', ti)
        if (ti.TeamIndex == Team)
        {
          ti.Score += CaptureScore;
          break;
        }

And finally award the person who did the actual capture.

      Scorer.PlayerReplicationInfo.Score += CaptureScore;
    }

DiscardInventory is called whenever a Pawn dies, and since we don't want a flag destroyed if a player dies we must override this to put it back to its original position. NOTE: It would be a good idea to expand this to drop the flag wherever they died and only send it back after a certain time or if it is touched by a player of the same team. (as in Quake2 CTF and others)

    function DiscardInventory(Pawn Other)
    {
      local Inventory Inv;

Use FindInvetoryType to get a reference to the flag if the pawn has it, and if so drop it.

      Inv = Other.FindInventoryType(class'ctf_ca.Flag');
      if (Flag(Inv) != None)
      {

DropFrom drops the Inventory item at the specified location and handles removing the item from the player's inventory.

        Inv.DropFrom(Flag(Inv).InitLocation);
      }

Let the normal DiscardInventory do its work now that we've done what we needed.

      Super.DiscardInventory(Other);
    }

    defaultproperties
    {
         CaptureScore=10
         bSpawnInTeamArea=True
    }

    //==================================================
    // FlagSpawnPoint - Flag Spawning Point
    //==================================================

This class should be placed where you want the flags to be in the level. The SpawnFlag() function is called at the beginning of the game and creates the actual flag. NOTE: Since FlagSpawnPoint is a subclass of NavigationPoint it will be part of the pathnode table of a level, and as such will be usuable by bots, so it should be relatively simple to alter Unreal's bots to play CTF.

    class FlagSpawnPoint expands NavigationPoint;

TeamNumber should be set to the owner Team

    var() byte TeamNumber;

MyFlag is a local reference of the flag spawned

    var Flag MyFlag;

SpawnFlag() just creates the flag, then sets the Team and InitLocation variables.

    function SpawnFlag()
    {
      MyFlag = spawn(class'ctf_ca.Flag');
      MyFlag.Team = TeamNumber;
      MyFlag.InitLocation = Location;
    }

    defaultproperties
    {
    }

    //===============================================
    // Flag - CTF Flag
    //===============================================

This is a normal Pickup item, and we alter the Pickup state (the Touch() function in particular) to check for flag captures...etc. It is created by a FlagSpawnPoint, and it handles the majority of the CTF game code itself.

    class Flag expands Pickup;

Team and Initial location

    var byte Team;
    var vector InitLocation;

    auto state Pickup
    {

Touch() is called whenever an Actor collides with this Actor, and normally for a Pickup item it would handle adding this item to the inventory of the Pawn if possible. We alter it to see if the PlayerPawn touching us has a flag (for a capture), or if the PlayerPawn is on an opposite team we give the flag to them like a normal item.

      function Touch( actor Other )
      {
        local Inventory Copy, EnemyFlag;

If it isn't a playerpawn then can't grab the flag. NOTE: Might be a good idea to change this to Pawn, or add a check for Bots if you want to add Bot support.

        if (!Other.IsA('PlayerPawn'))

          return;

If it is on the same team as us then go ahead and check for a capture.

        if (PlayerPawn(Other).PlayerReplicationInfo.Team == Team)
        {

Check for capture by seeing if they have a flag.

          EnemyFlag = Pawn(Other).FindInventoryType(class'ctf_ca.Flag');
          if (EnemyFlag != None)
          {

Score points, reset flag, and let the world know about it.

              BroadcastMessage(PlayerPawn(Other).PlayerReplicationInfo.PlayerName
                               $ " captured the enemy flag!");
              CTFGame(Level.Game).ScoreTeamCapture(Team, PlayerPawn(Other));
              Flag(EnemyFlag).DropFrom(Flag(EnemyFlag).InitLocation);
          }
          return;
        }

If they aren't on the same team then give the flag to them.

        else
        {

Give flag to enemy and let the world know about it.

          BroadcastMessage(PlayerPawn(Other).PlayerReplicationInfo.PlayerName
                           $ " stole the enemy flag!");
          bHeldItem = true;
          GiveTo(Pawn(Other));
          Pawn(Other).ClientMessage(PickupMessage, 'Pickup');
          PlaySound (PickupSound,,2.0);
        }
      }
    }

    defaultproperties
    {
         PickupMessage="Got the flag!"
         ItemName="Flag"
         PickupViewMesh=Mesh'UnrealI.Flag1M'
    }