| |
 |
|
|
  |
| CTF Mod: Part 2 | Intermediate |
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 2Download: 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 |
| |