I'm a doctor, not a mechanic

Legacy:WalknBullseye

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search

This is my contribution to the Unreal community.

I am pretty new to the coding aspect of gaming. So new in fact that I had know idea how the game actually worked, internally. I have had no previous coding experience whatsoever. Everything that I have learned is self taught thru this site. Without this resource it would have been impossible for me to have gotten to where I am now.

I started off modifying rifles for sniper/camper clans. I have progressed to the point where I have actually created a few mutators from scratch.

One thing that I have found difficult to do is actually get a mutator to work. Meaning to get the mutator assigned to a player and then getting the mutator to function.

I will use as an example a mutator that I recently created.

SID ( Slow It Down ) an Anti-Run mutator

Lets start off with our vars

Vars[edit]

class RunMonitor extends mutator config (SIDvX);
 
 
 
var pawn Runner;
struct PlayerInfo                 // Here we store all of the relevant info for the player
{                                 
       var int RT;               
       var int GPTime;            
       var bool bGP;              
       var int PlayerID;
       var string PlayerName;
       var int count;
       var int PCount;
       var int TeamID;
       var float PositionX,
                 PositionY;
       var bool bMoved;
       var bool bDamage;
};
var PlayerInfo PI[64];            // Now we create an array so each player can have their own info
 
 
var bool bInitialized,bbInit,Init2;

For my mutator I decided to use a struct to handle the storage of the relevant values. The reason I did, is a struct can hold any type of var in it. In my case I am going to have a lot of info for each player that I am going to have to use and keep track of. I could have used an array for each var type, like this

var int RT[64];
var int GPTime[64];            
var bool bGP[64];              
var int PlayerID[64];
var string PlayerName[64];
var int count[64];
var int PCount[64];
var int TeamID[64];
var float PositionX[64],
                 PositionY[64];
var bool bMoved[64];
var bool bDamage[64];

and got the same effect.

I found it easier to keep up with by using a struct and then creating an array of that struct

var PlayerInfo PI[64];

Setting the values[edit]

Now that we have the vars created we need to assign some values to them so we can use them

function FillList ()              // Here we fill our PlayerInfo array PI[ ]
{
 
 
    local Pawn PList;
    local int i,x;
 
 
	for (i=0; i<64; i++)         // First off lets fill the entire array with some default values
	{
		PI[i].PlayerID = -1;
		PI[i].PlayerName = "";
	}
	i = 0;
	for (i=0; i<4; i++)          // Same here
	{
	    TPI[i].TPCount = 0;
	    TPI[i].TWCount = 0;
	}
 
	i = 0;                                                                    // We need to fill our array with the correct values
	for ( PList=Level.PawnList; PList!=None; PList=PList.nextPawn )  // Level.Pawnlist is a list of all of the Pawn in a game
	{
		if	(	( PList.bIsPlayer && PList.class != class'Spectator' )   // We check to make sure we do not have a specator
			&&	( !PList.PlayerReplicationInfo.bIsSpectator )
			&&	( !PList.bHidden )
			)
		{
		    x = PList.PlayerReplicationInfo.PlayerID;   // We grab the player ID and assign to to 'x'
			PI[x].PlayerID = PList.PlayerReplicationInfo.PlayerID;  // Now we simply assign the values we want to the array PI[x].XXX
			PI[x].count = 0;
			PI[x].RT = RunTime;
			PI[x].PlayerName = PList.PlayerReplicationInfo.PlayerName;
			PI[x].PCount = 0;
			PI[x].PositionX = 0;
			PI[x].PositionY = 0;
			i++;
		}
	}
 
}

We now have a function that 'Fill' our array with all of the values we are going to need. Now we need to find some way of getting this function executed. A lot of mutators are started in

event PreBeginPlay()

or

event PostBeginPlay()

This won't work for me because I am basing my array entries on the playerID. At the time that Pre/PostBeginPlay are executed the players do not have IDs yet. So...

function ModifyPlayer(Pawn Other)     // When the player is spawned, this function is called
{                                     // here is a good place to 'set' the players info
    local int TAG;                    // We cant fill our list any earlier since until this point all of the players are specators
    if (!bInit)                       // We set a bool here to 'Fill' our player list
     {                                // using a bool we can make sure its only loaded once
        FillList();                    
        bInit = true;
     }
 
	if( (Other.IsA('PlayerPawn') ) && Other.bIsPlayer && !Other.IsA('Spectator'))      
	   {
	     Tag = PlayerPawn(Other).PlayerReplicationInfo.PlayerID;                
                 // We need to have some way of identifying the player so we can access the correct array entry
         if (PI[TAG].PlayerName == "" || (PI[TAG].PlayerName != PlayerPawn(Other).PlayerReplicationInfo.PlayerName))   
                // We need to verify the player spawning has the correct info
           {
               Update(PlayerPawn(Other));     // If the player name does not match the player name in the array, we update the array info
           } 
	      SetMessage ( class'SIDv5.RunMonitor'.default.Message[7], 7);
    	  PlayerPawn(Other).ReceiveLocalizedMessage( class'Warning2',7, );      
              // Give the player the grace period message when they spawn
 
	      PI[Tag].RT = RunTime;         // We assign the the modified players ID # to the value TAG 
	      PI[Tag].GPTime = GracePeriod;          // Now by usin the var TAG in the array designator [ ],we can assign the values we want
	      PI[Tag].PositionX = PlayerPawn(Other).Location.X;          // to the array that matches the player ID
	      PI[Tag].PositionY = PlayerPawn(Other).Location.Y;
	      PI[Tag].TeamID = PlayerPawn(Other).PlayerReplicationInfo.TeamID;
	      PI[Tag].bMoved = false;
	      PI[Tag].bDamage = true;
	   }
 
 
	super.ModifyPlayer(Other);
}

We have now assigned an array entry that corresponds to the playerID and filled to entry with all of info we want. The only thing we need to worry about now is, what happens if the player disconnects in the middle of the game? Right now the player will get a new array with the default values. Meaning, They get a fresh start. Lets see about fixing that.

if (PI[TAG].PlayerName == "" || (PI[TAG].PlayerName != PlayerPawn(Other).PlayerReplicationInfo.PlayerName))   
                // We need to verify the player spawning has the correct info
           {
               Update(PlayerPawn(Other));     // If the player name does not match the player name in the array, we update the array info

Lets take care of that here...

 
function Update (pawn P)                 // Here is where we update or player info when it doesnt match
{                                        
 
   local int i;                          
   local int ID;                         
 
   ID = P.PlayerReplicationInfo.PlayerID;         // Again we assign the player ID to a var 'ID'
 
 
          for ( i = 0; i < 64; i++)               // We cycle thru the array list 
             {
                 if ( P.PlayerReplicationInfo.PlayerName == PI[i].PlayerName   // If we find a playername that IS the same in the array
                     && P.PlayerReplicationInfo.PlayerID != PI[i].PlayerID     // AND the player ID IS NOT (!=) the same as the array #
                     && P.bIsPlayer)                                           // then we assign the values in the old array entry (PI[i])
                    {                                                          // to the new array entry (PI[ID]) 
                        PI[ID].count = PI[i].count;
                        PI[ID].PlayerName = PI[i].PlayerName;
                        PI[ID].PlayerID = ID;
                        PI[ID].PCount = PI[i].PCount;
                        PI[ID].RT = Runtime;                                   // Here we blank out the old name just for good measure
                        PI[i].PlayerName = "~~~~~~~~";
 
                    }
 
 
             }
 
 
 
}

Now if a player disconnects in an attempt to reset the mutator values and get a fresh start, they will bet their old values. Of course if they change their name it will reset their values. Oh well. At least they will have to do some more work.

Now that you have your arrays set, filled and ready to go... now what???

You need to use those values and actually do something with them.

This is how I used my newly created var array

Here is just a portion of the mutator (Too much stuff to put it all here)

       // Here is where the magic happens!!!
function Timer ()              // I decided to use the timer function    
{                              // In prebeginplay we set the timer cycle time
   local int R,T;              // settimer(1.5,true) 1.5=the time between cycles, True=does it repeat
 
 
 
 
 
for ( Runner = Level.PawnList; Runner != none; Runner=Runner.NextPawn) // Lets grab a player (Runner)
 {
  if (PlayerPawn(Runner) != none)
   {
    if   (!PlayerPawn(Runner).IsA('Spectator'))
     {
      if   (PlayerPawn(Runner).IsA('playerPawn') && !PlayerPawn(Runner).IsA('bot'))
       {
        R = PlayerPawn(Runner).PlayerReplicationInfo.PlayerID;  // We got a valid player
            // We can now use the valid players ID to 'tie' it to an array by 'seeting' the value R=PlayerID
        if (PI[R].PlayerName == "" || (PI[R].PlayerName != PlayerPawn(Runner).PlayerReplicationInfo.PlayerName))
         {
          Update(PlayerPawn(Runner));  // The player ID and Name dont match so we need to update the array
         }
        if (level.Game.bTeamGame)
         {
          T = PlayerPawn(Runner).PlayerReplicationInfo.Team;
         }
        if ( (PI[R].PositionX != PlayerPawn(Runner).Location.X) || (PI[R].PositionY != PlayerPawn(Runner).Location.Y) )
         {                       // Here we make a check on the players postition so we know if they have moved
          PI[R].bMoved = true;   // This way the grace period timer does not start until they move
         }
        if (PI[R].bMoved)        // Once they have moved the graceperiod time starts
         {
          PI[R].GPTime --;       // Every cycle of the timer will take 1 off of the var GPTime in the players array
          if ( PI[R].GPTime > 0 ) // If the value is over the GPtime we send the player a message of what the current count is
           {
            PlayerPawn(Runner).ReceiveLocalizedMessage( class'Warning3',PI[R].GPTime, );
            break;                // Here I use the the value of GPTime to determine which message get displayed
           }                      // Using 'break' here will stop any more of the code from this function from getting executed
                                  // Otherwise the engine will exit out this 'if' statment and continue on to the next one.
          if ( PI[R].GPTime == 0 )  // At 0, the player gets a message telling them GP is over
           {
            SetMessage ( class'SIDv5.RunMonitor'.default.Message[8], 8);
            PlayerPawn(Runner).ReceiveLocalizedMessage( class'Warning2',8, );
            PI[R].bGP = true;     // Once the GPTime gets to 0 we set the vlaue bGp to true so it no bypasses this section
           }
          }

That is just a small example of how I got my mutator to work. Using this example, you will be able do many different types of things.

Tarquin: Hi, welcome to Unreal Wiki :)

WalknBullseye: Thanks, good to be here