[Tutorial] Creating a Basic Mutator

  • Please make sure you are familiar with the forum rules. You can find them here: https://forums.tripwireinteractive.com/index.php?threads/forum-rules.2334636/

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Prerequisites: Killing Floor SDK (Steam->Library->Tools)

Creating a Basic Mutator

Step-by-step Instructions

1. Create directory hierarchy

Create a directory in .../steamapps/common/killingfloor with the same name you wish to give your mutator. Create a subdirectory inside this named Classes. You should now have a directory structure that looks like this:

.../steamapps/common/killingfloor/ExampleMutator/Classes

2. Create main mutator script file

Inside the classes subdirectory create a file with a .uc extension with a name matching the mutator's main directory name, for example ExampleMutator.uc. The path of this file should look like this:

.../steamapps/common/killingfloor/ExampleMutator/Classes/ExampleMutator.uc

3. Tell compiler we want to include this mutator in the compilation

In order to have the compiler actually compile your mutator you must add an entry for it in a file named killingfloor.ini, located in the game's system directory. Open this file, search for the section with several lines beginning with EditPackages=PackageName. Add an entry at the bottom of this list to include the name of your mutator package, which should look like this:

EditPackages=ExampleMutator

4. Add some code to the mutator script

Code:
class ExampleMutator extends Mutator;

defaultproperties
{
    GroupName="KFExampleMutator"
    FriendlyName="Example Mutator"
    Description="Mutator description here"
}

This is the bare minimum for compiling a mutator that can be seen in Killing Floor (actually, FriendlyName and Description aren't strictly necessary but why compile without them?). The GroupName property must start with the letters KF else it won't be seen by the game.

5. Compile using UCC

Compiling the mutator is as simple as calling ucc make, which can be found in the system directory (in fact the file is simply called ucc.exe and make is a command line argument). Note that if you have previously compiled the mutator you must remove its .u file from the system directory in order to recompile (otherwise the mutator will be ignored in the compilation process).

If you cannot find ucc.exe you may not have the Killing Floor SDK installed. Find it on Steam in the 'tools' section.

Additionally you may want to remove certain files and have the console wait before closing, so that you can view the compilation output (and thus determine if it failed and why). You could do all this by opening command prompt and typing the commands manually, but it's far simpler to use a batch file. Create a batch file (anywhere - the desktop is good for easy access) with a name such as ExampleMutator.bat and copy the following lines in:

Code:
del "C:\Program Files\Steam\steamapps\common\killingfloor\System\ExampleMutator.u"
"C:\Program Files\Steam\steamapps\common\killingfloor\System\UCC.exe" make
del "C:\Program Files\Steam\steamapps\common\killingfloor\System\steam_appid.txt"
pause

You may need to alter the paths if you have the game installed to a different hard drive or have a different directory structure. The third line removes a file generated by UCC which while exists will prevent connecting to servers (may not apply to all).

Run the batch file and tada! If compilation was successful, you have just made your first mutator. If not, you have made a mistake.

6. Functional mutator example

Here is a functional, more interesting mutator to take a look at:

Code:
class ExampleMutator extends Mutator;

function PostBeginPlay()
{
	SetTimer(1, true);
}

function Timer()
{
	local KFHumanPawn Player;
	
	foreach DynamicActors(class 'KFHumanPawn', Player)
	{
		if (Player.Health + 2 <= Player.HealthMax) Player.Health += 2;
		else Player.Health = Player.HealthMax;
	}
}

defaultproperties
{
    GroupName="KFExampleMutator"
    FriendlyName="Example Mutator"
    Description="Mutator description here"
}

What does it do? It increases the health of each player by 2 points every second.

7. Where do I go from here?

Now that you understand how to compile a mutator, I'd recommend first taking a look at the official UnrealScript Language Reference which covers all aspects of the language itself. If you plan to write mutators which work online it's vital that you understand how the networking is handled, and I'd suggest reading the Unreal Networking Architecture article on this. If you want to know more about the engine (and particularly which classes are available to use) you can either browse through the script files yourself, or check out the BeyondUnreal Wiki.

I'd recommend getting a good text editor with support for line numbers (I suggest Notepad++ for this) and a search tool for searching the entire library of script files for finding where specific classes and variables are defined (I'd recommend Windows Grep as it's powerful, fast, and free).
 
Last edited:

Prodigy

FNG / Fresh Meat
Feb 28, 2010
239
4
0
Umbrella Corp. HQ
should add a function to customize the regen amount, otherwise GOOD JOB.

john was talking about doing this, and so was i, but now that it's been done, we dont have to :D

keep up the good work.
 

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Well, I wanted to keep it as simple as possible - I did actually edit a regen mutator I made which allowed customizing the regen speed and amount. Thanks, I plan to do some more (such as covering how to run code client-side and what bAddToServerPackages is used for).

I'm not exactly fluent when it comes to UnrealScript but I'd like to teach what I know and get other people interested. This forum needs more coders!
 

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
how do i create a mutator to modify specimen properties? (e.g. increase patriarch health, etc. ) i know that for players modifyplayers is used but what about the specimens?
 

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
You can simply change a 'defaultproperties' variable of the relevant class when the mutator is loaded (in the PostBeginPlay function), like so:

Code:
class'ZombieBoss'.default.HealthMax = 9999;

This way when an enemy of this class is spawned (in this case the patriarch) it will use this value by default. Have a look in the KFChar folder for the specimen class files, and figure out what you can change by looking at the defaultproperties section. Also see this thread.
 
Last edited:

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
i tried your suggestion.

"Error, Unrecognized member 'defaultproperties' in class 'Class'"

speed doesn't seem to be in kfchar but rather in kfmod. also i tried both "class'ZombieClotBase'.default.GroundSpeed = 150;" and "class'ZombieClotBase'.default.GroundSpeed = 150;" in event PostBeginPlay() but they were still slow. :confused:
 

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Sorry, made a little mistake up there and it should have been 'default' instead of 'defaultproperties'.

When changing a default variable make sure you change it for the most derived class (which in the case of a clot is ZombieClot).

Try setting ZombieClot's default GroundSpeed variable instead.
 
Last edited:

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
Thanks I'll try that when I get home. I'm at work right now. Your antiblock mutator does not require to be downloaded by clients. Is it possible for me to make a mutator that modifies the specimens as mentioned above without the clients having to download the mutator? Are there lines of code I can add to ensure that they don't download it? I want the gameplay to surprise the players. Thanks.
 

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Actually, it's simply a matter of not adding a particular line of code:

Code:
bAddToServerPackages=true

And also don't manually add it to server packages either!

Is it possible for me to make a mutator that modifies the specimens as mentioned above without the clients having to download the mutator?

Yes, since the server decides the attributes of the specimens you can alter these attributes just on the server and the changes will be replicated to clients. However, you have to do more than just alter defaultproperties. You see, the problem is that both the server and the client have their own copy of these attributes, and changes to the default values are not replicated, meaning if you change the default GroundSpeed variable on the server, the client will still think it's the original. You'll instead want to change the variables of each specimen as they spawn, which you can do by implementing the CheckReplacement function:

Code:
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
	if (ZombieClot(Other) != None)
	{
		ZombieClot(Other).GroundSpeed = 10;
	}
	
	return Super.CheckReplacement(Other, bSuperRelevant);
}

However, there's another problem. Certain variable defaults are used during gameplay, which will reset sometimes (for instance uncrouching resets GroundSpeed to the default). I'd suggest that for such variables you simply set the default properties on both the server and the client.
 

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
I see. Does that mean I should use both the CheckReplacement function and change the default groundspeed as you've mentioned?

"I'd suggest that for such variables you simply set the default properties on both the server and the client."

CheckReplacement does this already right? Or do I have to do some other coding?

"or instance uncrouching resets
GroundSpeed to the default"

Since the specimens do not crouch, I don't have to worry about the groundspeed resetting, do I? I'm kind of lost as far as covering all bases to make sure it works.

Thanks for all these info Benjamin. :)
 
Last edited:

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Well, that was just an example. There are other reasons for the specimen speed being temporarily altered, such as when attacking a door. For what you want you'll need to have both the server and client execute the mutator functions, and alter the default properties.

If you want to know if and when certain variables are changed, run WinGrep and search for the desired variable with a "*.uc" file filter in the killingfloor directory.
 

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
Well, that was just an example. There are other reasons for the specimen speed being temporarily altered, such as when attacking a door. For what you want you'll need to have both the server and client execute the mutator functions, and alter the default properties.

If you want to know if and when certain variables are changed, run WinGrep and search for the desired variable with a "*.uc" file filter in the killingfloor directory.

O.O This is more complicated than i thought as I don't know how to have the server and client execute the mutator functions. I thought CheckReplacement was the solution. I think I only know how to do so on the server side. I don't know how to reset the groundspeed back to what it was if the specimens try to break down the door. Groundspeed is very complicated and maybe not worth the trouble for a newbie like me. hehe. For specimen health, do I use checkreplacement or do I use the command you mentioned? (class'ZombieBoss'.default.HealthMax = 9999;) This might be an easier mod to make than groundspeed.
 

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
For HealthMax I'd recommend changing the default value. It won't match up with the default value on the client, but that shouldn't be an issue since the client never has to deal directly with health variables. I can only think of one thing that'd be strange for clients, and that's that in the cutscene for the patriarch the original HealthMax value will show in the corner. If you don't want this just use CheckReplacement instead (but not both). I think you'll need to alter Health too.

As I said before, check out the Multiplayer Mutators tutorial, it explains how to run functions client-side.
 
Last edited:

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
Thanks for the reply. I've been learning a lot as of late and I'm almost finished with my mutator. Is it possible for a mutator to execute a function or a command depending on which wave it is? Thanks. :)
 
Last edited:

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
Yeah, this is possible. For example, if you want to run per-wave code every timer call you can do something like this:

Code:
function Timer()
{
	local KFGameType KF;
	
	KF = KFGameType(Level.Game);
	if (KF == None || !KF.bWaveInProgress) return;
	
	if (KF.WaveNum == 1) {
		// Handle wave 1 stuff
	}
	if (KF.WaveNum == 2) {
		// Handle wave 2 stuff
	}
}
 

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
That's good news. The only problem I have is that I already use Timer() to change the maxplayers.

Code:
function PostBeginPlay()
{
    // some code here to set specimen health
    SetTimer(1, false);
}

function Timer()
{
    Level.Game.MaxPlayers = 12;
}
Does this mean I have to create a separate mutator for handling per wave code? Also, what would be a good interval for this per wave function? Thanks again.
 
Last edited:

Benjamin

Grizzled Veteran
May 17, 2009
3,650
635
113
France
You could either use states or simply use a variable to indicate what should happen:

Code:
function PostBeginPlay()
{
	// some code here to set specimen health
	SetTimer(1, true);
}

function Timer()
{
	if (MyVar == 0) {
		Level.Game.MaxPlayers = 12;
		MyVar = 1;
	}
	else
	{
		// Handle other stuff here
	}
}

As for what frequency what timer should be called, it depends entirely on what you want it to do.
 

krispyk

FNG / Fresh Meat
Mar 15, 2010
208
31
0
Edit: I think I've figured the timeout part. Thanks for everything Benjamin. What I'll do now is do some tests to see cpu/memory usage and how it affects pings. Thanks again.
 
Last edited:

Phada

FNG / Fresh Meat
Aug 24, 2010
190
62
0
France
phada.2ya.com
Nice tutorial, but I think you shouldn't use "foreach DynamicActors" to get playerpawns:

Code:
function Timer()
{
    local Controller C;
    local KFHumanPawn Player;

    for (C = Level.ControllerList; C != None; C = C.NextController) {
        if (C.Pawn != None && KFHumanPawn(C.Pawn) != None) {
            Player = KFHumanPawn(C.Pawn);
            // stuff
        }
    }
}