Tripwire Interactive Forums

Go Back   Tripwire Interactive Forums > Killing Floor Forums > Killing Floor Modifications > Coding

Reply
 
Thread Tools Display Modes
  #1  
Old 06-12-2010, 11:44 PM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default [Tutorial] Creating a Basic Mutator

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 by Benjamin; 05-25-2012 at 05:32 AM.
Reply With Quote
  #2  
Old 06-13-2010, 06:02 PM
Prodigy Prodigy is offline
On Vacation
 
Join Date: Feb 2010
Location: Umbrella Corp. HQ
Posts: 239
Default

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

keep up the good work.
Reply With Quote
  #3  
Old 06-13-2010, 08:18 PM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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!
Reply With Quote
  #4  
Old 06-30-2010, 07:08 AM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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?
Reply With Quote
  #5  
Old 06-30-2010, 07:19 AM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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 by Benjamin; 06-30-2010 at 08:31 AM.
Reply With Quote
  #6  
Old 06-30-2010, 07:59 AM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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.
Reply With Quote
  #7  
Old 06-30-2010, 08:33 AM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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 by Benjamin; 06-30-2010 at 11:38 AM.
Reply With Quote
  #8  
Old 06-30-2010, 05:29 PM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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.
Reply With Quote
  #9  
Old 06-30-2010, 07:51 PM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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!

Quote:
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.
Reply With Quote
  #10  
Old 06-30-2010, 09:32 PM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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 by krispyk; 06-30-2010 at 09:45 PM.
Reply With Quote
  #11  
Old 06-30-2010, 09:56 PM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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.
Reply With Quote
  #12  
Old 06-30-2010, 10:19 PM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

Quote:
Originally Posted by Benjamin View Post
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.
Reply With Quote
  #13  
Old 06-30-2010, 10:31 PM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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 by Benjamin; 06-30-2010 at 10:42 PM.
Reply With Quote
  #14  
Old 07-01-2010, 03:40 AM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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 by krispyk; 07-01-2010 at 04:07 AM.
Reply With Quote
  #15  
Old 07-01-2010, 05:21 AM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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
	}
}
Reply With Quote
  #16  
Old 07-01-2010, 06:12 AM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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 by krispyk; 07-01-2010 at 06:14 AM.
Reply With Quote
  #17  
Old 07-01-2010, 09:11 AM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

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.
Reply With Quote
  #18  
Old 07-01-2010, 09:38 PM
krispyk krispyk is offline
Senior Member
 
Join Date: Mar 2010
Posts: 208
Default

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 by krispyk; 07-02-2010 at 04:16 AM.
Reply With Quote
  #19  
Old 07-02-2010, 04:57 AM
Benjamin Benjamin is offline
Senior Member
 
Join Date: May 2009
Location: France
Posts: 3,430
Default

I'd suggest simply using the lobby timeout setting in the server rules.
Reply With Quote
  #20  
Old 09-06-2010, 12:34 PM
Phada's Avatar
Phada Phada is offline
Senior Member
 
Join Date: Aug 2010
Location: France
Posts: 190
Default

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
        }
    }
}
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 03:19 PM.


Powered by vBulletin®
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Copyright ©2005 - 2013, Tripwire Interactive, LLC