- unrealed.net
  Main Page
  Editing News
  Help Wanted
  Sharky's Tips
  Submitions
  Links
  Staff
   - Levels
  Main Page
  Tutorials
  Reference
   - UnrealScript
  Main Page
  Tutorials
  Reference
  Ask the Codemaster
   - Meshes
  Minor Nerve Damage
   - #unrealed
#unrealed on GamesLink (irc.gameslink.net) is the official channel for unrealed.net. Already a steady group of people are there helping out each other with Unreal editing. Stop by and share your knowledge.
   - hosted sites
  The 3D Model Emporium
  Weapons of Destruction
  Shambler's Prefabs
  Snarf's U.M.E.
  T.P.T.Avalon TC
  Rage TC


   Hosting
Interested in having your site hosted here? Goto the Hosted Sites page and find out what we offer in order to give your work a permanent home.
   - Your Unrealed.com
   Submitting
Want to have your latest tutorial, tip, or general info on Unrealed.com? Just send it on over. We will be glad to post it on the site. Also, feel free to request a tutorial, because we can't give you what you want, without you telling us what you want.
   - network sites
  Unreal Nation
  World of Dreams
   - noteworthy sites
  Unrealized
  SFX (Sound FX)
 

 
CTF Mod: Part 2Intermediate
Alright, last time we created a teamless version of capture the flag, we focused on adding the flags to Unreal. Now, we add the teams to Unreal. Unreal already has a teamplay mode, and we can use already built in classes to our advantage. To start, we will create a subclass of DeathMatchGame which we will name CaptureTheFlag, with unctf as the Package name (Info->GameInfo->UnrealGameInfo->DeathMatchGame).

Because this is capture the flag, which has a red and blue team, we need to create two teams at the beginning of the game. TeamInfo is a subclass of Info and contains the following variables:

var string[32] TeamName;
var int Size; //number of players on this team in the level
var float Score;

That works great for us, we don't need to anything else to that, so we'll use it. So, at the beginning of CaptureTheFlag, we put the following line:

var TeamInfo RedTeam, BlueTeam; So then, in the BeginPlay() function which we use to initialize the variables, we spawn the red and blue team, setting their size and score to 0. We make sure the call, Super.BeginPlay(), because we still want all the functionality in the BeginPlay() functions of the superclasses:

function BeginPlay()
{ RedTeam = Spawn(class'TeamInfo');
RedTeam.Size = 0;
RedTeam.Score = 0;

BlueTeam = Spawn(class'TeamInfo');
BlueTeam.Size = 0;
BlueTeam.Score = 0;

Super.BeginPlay();

}

There are two functions we need to override from the DeathMatchGame class, Login() and Logout() which are called when a player leaves or joins a game, add the following to your CaptureTheFlag class, note the comments for explanations of what each line does:

function playerpawn Login
( string[32] Portal,
string[120] Options,
out string[80 ] Error,
class SpawnClass
)
{ local PlayerPawn newPlayer;
local string[64] InTeam;

newPlayer = Super.Login(Portal, Options, Error, SpawnClass);
if ( newPlayer == None)

return None; newPlayer.Team = 0; // Team is a byte of the Pawn class, we set it to 0 for blue, 1 for red, on joining the game, the player is automatically set to the blue team
return newPlayer;
}

function Logout(pawn Exiting)
{

if (Exiting.Team > 0) // If the player leaving was on the red team....
RedTeam.Size--;
else // else they were on the blue team
BlueTeam.Size--;

Super.Logout(Exiting);

}

Ok, now when the player joins the game, they are set on a team. But, how would you know which player was on which team? Well, in Capture the Flag, each team uses the same color skin as their flag, so we need to make a few modifications to the Login() function so that it sets the skin to the color of the team:

function playerpawn Login
( string[32] Portal,
string[120] Options,
out string[80 ] Error,
class SpawnClass
)
{ local PlayerPawn newPlayer;
local string[64] InTeam;
local texture NewSkin; //Added to set the skin of the player to his team's color

newPlayer = Super.Login(Portal, Options, Error, SpawnClass);
if ( newPlayer == None)

return None; newPlayer.Team = 0; // Team is a byte of the Pawn class, we set it to 0 for blue, 1 for red, on joining the game, the player is automatically set to the blue team

NewSkin = texture(DynamicLoadObject(string(newPlayer.Mesh)$"Skins.T_Blue", class'Texture')); // Set the NewSkin variable to the blue skin since he is automatically on the blue team
newPlayer.Skin = NewSkin; // Set the player's skin to NewSkin

return newPlayer;

}
Alright, now we look like a team! But, the flag still doesn't know which team your on, so we'll need to make some modifications to CTFFlagRed and CTFFlagBlue. So, open the CTFFlagBlue, and now we check to see if PlayerPawn(Other).team variable to determine which team the player is on, make the following changes to the CTFFlagBlue Touch() function, note that we revamped the score message to keep track of the score for each team:

if ( ValidTouch(Other))
{ if (PlayerPawn(Other).Team != 0)
{
if (Pawn(Other).FindInventoryType(class'CTFFlagRed') == None)
{ Copy = SpawnCopy(Pawn(Other));
Pawn(Other).ClientMessage(PickupMessage); // add to inventory and run pickupfunction
PlaySound (PickupSound,,2.0);
PickupFunction(Pawn(Other));
}
}
else
{
if (Pawn(Other).FindInventoryType(class'CTFFlagRed') != None && location == Spawner.location)
{ CaptureTheFlag(Level.Game).BlueTeam.score += 1.0;
Pawn(Other).ClientMessage("Blue Team Score: "$int(CaptureTheFlag(Level.Game).BlueTeam.score));

Inv = Pawn(Other).FindInventoryType(class'CTFFlagRed');
if ( Inv != None ) Inv.Destroy(); CTFFlag(Inv).Spawner.SpawnFlag();
}
}
}
And do similar changes to the CTFFlagRed function, except check to see if PlayerPawn(Other).team is equal to 0 instead of being not equal to it (use if(PlayerPawn(Other).Team == 0) ).

We have now finished adding teams to our Capture the flag mod. But, we've forgot some functionality in our ctf mod. You see, when a player dies, they need to drop the flag next to their body. So, in the Pawn class, there is a class variable named DropWhenKilled. So, in our Touch() function, we need to set the DropWhenKilled variable to the flag, open the CTFFlagBlue, and make these changes:

if (Pawn(Other).FindInventoryType(class'CTFFlagRed') == None)
{ Copy = SpawnCopy(Pawn(Other));
Pawn(Other).ClientMessage(PickupMessage); // add to inventory and run pickupfunction
PlaySound (PickupSound,,2.0);
PickupFunction(Pawn(Other));
Pawn(Other).DropWhenKilled = class'CTFFlagBlue';
}
Set the DropWhenKilled to class'CTFFlagRed' in the CTFFlagRed class instead of class'CTFFlagBlue'. And then, when the player captures the flag, we need to set their DropWhenKilled variable to none so that they won't drop a flag now that we've destroyed the flag:

if (Pawn(Other).FindInventoryType(class'CTFFlagRed'))
{ CaptureTheFlag(Level.Game).BlueTeam.score += 1.0;
Pawn(Other).ClientMessage("Blue Team Score: "$int(CaptureTheFlag(Level.Game).BlueTeam.score));
Inv = Pawn(Other).FindInventoryType(class'CTFFlagRed');
if ( Inv != None ) Inv.Destroy(); CTFFlag(Inv).Spawner.SpawnFlag();
Pawn(Other).DropWhenKilled = None;
}
Unfortunately, when a new flag is spawned after a person dies, it looses the Spawner variable, so we need to override the DiscardInventory() function in the CaptureTheFlag class:

function DiscardInventory( Pawn Other )
{ local actor dropped;
local inventory Inv, Flag; // Added the flag variable which will be set the flag in the person's inventory
local weapon weap;
local float speed;

if( Other.DropWhenKilled != None )
{

dropped = Spawn(Other.DropWhenKilled,,,Other.Location);
Inv = Inventory(dropped);
if ( Inv != None )
{ Inv.RespawnTime = 0.0; //don't respawn
Inv.BecomePickup();
} if ( dropped != None )
{ dropped.RemoteRole = ROLE_DumbProxy;
dropped.SetPhysics(PHYS_Falling);
dropped.bCollideWorld = true;
dropped.Velocity = Other.Velocity + VRand() * 280;
} if ( Inv != None ) Inv.GotoState('PickUp', 'Dropped'); Flag = Other.FindInventoryType(class'CTFFlagRed'); // Set flag equal to the flag person possesses
if ( Flag != None ) // If they had a red flag
{ CTFFlag(dropped).Spawner = CTFFlag(Flag).Spawner; // Set the newly spawned flag sitting next to the dead body's Spawner variable = to the flag in the inventory's Spawner variable
Flag.Destroy();
}

Flag = Other.FindInventoryType(class'CTFFlagBlue'); // Set Flag to the blue flag
if (Flag != None) // if they had the blue flag
{

CTFFlag(dropped).Spawner = CTFFlag(Flag).Spawner; // Set the newly spawned flag sitting next to the dead body's Spawner variable = to the flag in the inventory's Spawner variable
Flag.Destroy();
}
} if( Other.Weapon!=None && Other.Weapon.Class!=DefaultWeapon )
{ speed = VSize(Other.Velocity);
weap = Other.Weapon;
weap.Velocity = Normal(Other.Velocity/speed + 0.5 * VRand()) * (speed + 280);
Other.TossWeapon();
if ( weap.PickupAmmoCount == 0 ) weap.PickupAmmoCount = 1; Other.Weapon = None;
} Other.SelectedItem = None;
for( Inv=Other.Inventory; Inv!=None; Inv=Inv.Inventory ) Inv.Destroy();
}
So, now when the Player carrying the flag dies, they drop it. But, what happens if a Player touches a flag that's been dropped? If it's a player with a different color from the flag, they will pick it up just as if it was back at the original location, but if that is the player's flag, we want to return the flag back to it's original location when they touch it. So, we need to modify the Touch() function in CTFFlagRed and CTFFlagBlue, to add the following lines (example is CTFFlagBlue). In addition, we added a check to see if the Flag was at it's original location (cause you can only capture the flag there). Make sure to reverse any references to the blue or red team or flag when editing the CTFFlagRed, see the sample script if your having trouble:

else
{ if (Pawn(Other).FindInventoryType(class'CTFFlagRed') != None && location == Spawner.location)
{ CaptureTheFlag(Level.Game).BlueTeam.score += 1.0;
Pawn(Other).ClientMessage("Blue Team Score: "$int(CaptureTheFlag(Level.Game).BlueTeam.score));
Inv = Pawn(Other).FindInventoryType(class'CTFFlagRed');
if ( Inv != None ) Inv.Destroy(); CTFFlag(Inv).Spawner.SpawnFlag();
Pawn(Other).DropWhenKilled = None;
}
else if ( location != Spawner.location)
{ Pawn(Other).BroadcastMessage(PlayerPawn(Other).PlayerName$" Returned the Flag",true);
Spawner.SpawnFlag();
Destroy();
}
}
So, we have the flag destroy itself, and tell it's FlagSpawner to spawn a new flag. There you have it, a complete basic CTF mod, gameplay wise. The only part left is to add an interface, with a setup menu, the ability to change teams, and a working scoreboard, we will go into that on Part 3.

Now, because we've added new game type (CaptureTheFlag), we need to modify our flagtest.unr test map, to run the ctf gameinfo by default, you do this by going to Level Properties (F6), and then LevelInfo, and put for DefaultGameType: Class'unctf.CaptureTheFlag'. Save your level and have fun.

Sources:

Tim Sweeney's UnrealScript Reference - Print This Out! It's a life saver
GreenMarine and his Beatdown Mod - I'm very thankful for all his expert advice

Tutorial : CTF Mod
Creating a Capture the Flag Mod for Unreal, Part 2

Download: unctf2.zip
Size:7kb

Author: Stonage
Date:July 17th, 1998
Sample Map:Yes



Site design and graphics by Matt Washer
Unreal and UnrealED are copy Epic Megagames, 1998