OBWANDO
Well heres my small stamp on the unreal world... Basically I'm going to document as I learn different areas and try to move them to places where they can be used, or for others to be able to find a 'shortcut' to certain things. My main focus is pretty much writing ut2003 mods, not mutators, but maybe muts later on.
If I write something screwy, just comment it in. I'm sure theres always a better way.
First thing is to start here: Mod Authoring For UT2K3 So I put it into my /ut2003 folder (ex: /ut2003/RollerBall2169)
Next you need to create the game class you plan on deriving from. In my case I used xBombingRun...
class RollerBallGame extends xBombingRun;
and saved it into /ut2003/RBall/RollerBallGame.uc
The important thing is that the class name must match the filename.
You can specify a new HUD, PlayerController, game name, and other things in here, but we'll go into that later.
Next thing you need to do is create an .int file, and add the reference to the packages loaded.
The INT File (.int) will specify a lot of things about your mod. You will place this file into your /ut2003/system folder
In this case I called it RollerBall2169.int and heres the contents. I read the .int page, but got confused a bit with the pipes, so there is an example of what it looks like with the pipes (A|B|C|D|E).
[PUBLIC] Object=(Class=Class,MetaClass=Engine.GameInfo,Name=RollerBall2169.RollerBallGame,Description="RB|RollerBall 2169|xinterface.Tab_IABombingRun|RollerBall2169.MapListRollerBall|true")
Notice I'm using a custom MapList and Map prefix. The prefix serves as to filter out maps in your list. If I used BR I would get all bombing run maps.
Next thing is to put your mod's file into the ut2003.ini file at the end of [Editor.EditorEngine]. In this case the entry was: EditPackages=RollerBall2169
now you have a basic framework to start from.
My mod is pretty simple by theory here. I want to remove all shooting weapons, use gliding skates, add some speed, do some ragdoll stuff, add some bludgeoning weapons, powerups, and of course a new ball and goals. Another item is that I want to make sure people stay on the course (people play on a track) and dont try to go backwards or stay off of the track.
I'll break down how I achieved some of the effects and what classes I modified and how I got them to work. You can extend any class you want, but dont expect it to be called or ever receive an event unless you get it plugged into your game correctly. Learn to use the Debugging Techniques, they will make your life much easier.
I use the DisplayDebug for my routines so I can show that I am getting calls to each class correctly. Broadcast to yourself events, you can get instant response with triggering or performing actions.
Drawing on the Canvas is easy, and if you want to simply put something on the next line in the DisplayDebug use:
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos) { super.DisplayDebug(Canvas, YL, YPos); Canvas.SetDrawColor(255,255,255); // Change colors for routines Canvas.DrawText("Player's name is "$PlayerOwner.PlayerReplicationInfo.PlayerName,false); // show: Player's name is Player YPos += YL; // move to the next line Canvas.SetPos(4,YPos); // and set the next position for the next drawtext. }
Im going to move ahead here and put things as I go along, As I move the items out into the open I will link them from here.
Now we're going to talk about controlling your (x)player and playercontrol. You can find the tree here at the Actor Class Hierarchy.
The hiearchy we are looking at is: Actor→Controller→]→[[UnrealPlayer→xPlayer.
The PlayerController is the instruction that is given a possessed Pawn. Think of the PlayerController as the mind and soul and the pawn as the physical body. You possess the pawn and control it to do whatever you want. Also, if the computer is playing, it uses the AIController and takes over the pawn in the same fashion as I just explained. (Think Matrix here)
The idea here was to allow the person to 'glide' along the surface which means no fast stopping, and accelerated motion. To do this you need to modify the PlayerController's PlayerTick. Why? because you want to have it check for controller changes and accelerate instead of applying motion immediately at each player time interval. This means we need to modify how the PlayController changes the acceleration and how the pawn will apply this acceleration. We're going to subclass the xPlayer class here since it has all of the same class info inhereted from its parent classes (more importantly to us the PlayerController class). Why didnt we just go for the PlayerController class? Simply because we only want to make a slight change and dont want to change how UnrealPlayer and xPlayer interact with the PlayerController. If we take over a function then we have to make sure all child classes are taken care of as well or you may get into big trouble.
To first get the engine to even send our new class some events we need to specify the PlayerController class and also specify our Pawn class (which has our PlayerMove function) that we are going to be using. If you remember we created a RollerBallGame which extended xBombingRun. It had our gamename and HUDType to show a custom HUD. Well we need to add a new line in there, so here it is:
// RollerBallGame.uc class RollerBallGame extends xBombingRun; defaultproperties { GameName="RollerBall 2169" HUDType="RollerBall2169.RollerBallHUD" PlayerControllerClassName="RollerBall2169.RBxPlayer" // This is our new custom PlayerController class. }
I'm sure you're saying... Wait a second, didn't you say that we werent going to modify the PlayerController class???
Thats correct, we haven't... We gave it a class that is derived from the PlayerController class and has all of the inhereted functions and variables of the PlayerController class, but only the functions we wanted to modify were changed. The class we are using here is the xPlayer class. Look it up in the hiearchy again, you will understand.
So heres is the patch you put in the front of the event:
// RBxPlayer.uc file class RBxPlayer extends xPlayer; simulated event PlayerTick( float DeltaTime ) { local int CurrentKey, DiffKey; // the first 5 lines here are from the PlayerTick in the PlayerController.uc file. local int c, i; // We still need the logic in PlayerController to apply, local bool bFullBuffer,bMatch; // so we copy the whole event in here and modify to suit our taste. Super.PlayerTick(DeltaTime); if (aForward > 0) AccelSpeed += AccelSpeedRate; else if (aForward < 0) AccelSpeed -= AccelSpeedRate; if ( (aForward == 0) && (AccelSpeed != 0) ) { if (AccelSpeed >0 ) { AccelSpeed -= AccelSpeedDecay; } else { AccelSpeed += AccelSpeedDecay; } if (AccelSpeedDecay > abs(AccelSpeed)) AccelSpeed = 0; // if we're inside of the decay zero it out } if ( abs(AccelSpeed) > Pawn.AccelRate ) { // make sure they arent going faster than the possible acceleration speed. if (AccelSpeed<0) { AccelSpeed = -Pawn.AccelRate; } else { AccelSpeed = Pawn.AccelRate; } } // ... And the rest of the event goes under here. // and at the tail of the file you need this added so that it will use our custom Pawn for other attributes we'll address later on. defaultproperties { AccelSpeedRate=50.00 // this is just for acceleration so that every playertick that happens it will accelerate by this interval. PawnClass=Class'RollerBall2169.RBxPawn' // This is the pawn we want to control not the default xPawn. Ill explain later. }
In case you are curious, the maximum acceleration rate of a pawn and other important stuff can be found in Pawn.uc:
// Movement. var float GroundSpeed; // The maximum ground speed. var float WaterSpeed; // The maximum swimming speed. var float AirSpeed; // The maximum flying speed. var float LadderSpeed; // Ladder climbing speed var float AccelRate; // max acceleration rate var float JumpZ; // vertical acceleration w/ jump var float AirControl; // amount of AirControl available to the pawn var float WalkingPct; // pct. of running speed that walking speed is var float CrouchedPct; // pct. of running speed that crouched walking speed is var float MaxFallSpeed; // max speed pawn can land without taking damage (also limits what paths AI can use) var vector ConstantAcceleration; // acceleration added to pawn when falling
Once we do that modification there we need to change the PlayerMove as well. The PlayerMove is part of the Pawn.uc class. But there are multiple States a player could be in. The player could be 'PlayerWalking', 'PlayerSwimming', etc... Each state controls how the player will move. If you are flying, you arent doing the same motions as if you were walking or swimming. Well, we're interested on how we walk, or in this case glide. Since we never plan on having our guy walk, we will simply modify the PlayerMove inside of the PlayerWalking state.
Tonight's dilemma was about bot control. I ride on skates, they walk. When I get hit, I fall down, crumple, whatever my karma body feels like (until i do the vectors for the correct hits (this weekend ). They dont. SO how do i fix this?
To change a bot you first need to extend the xBot. Problem is where is it called from? I tried changing the default class to my custom bot class (RBxBot) and no gold. Still went to xGame.xBot. So that means that I need to dig deeper...
Lets think about this. Almost everything we play derives from the deathmatch class. (insert class heiarchy here) So, we need to figure out what actually cranks a bot up!
After digging around, I found this little nugget inside of the deathmatch.uc file:
/* Spawn and initialize a bot */ function Bot SpawnBot(optional string botName) { local Bot NewBot; local RosterEntry Chosen; local UnrealTeamInfo BotTeam; BotTeam = GetBotTeam(); Chosen = BotTeam.ChooseBotClass(botName); if (Chosen.PawnClass == None) Chosen.Init(); //amb // log("Chose pawn class "$Chosen.PawnClass); NewBot = Bot(Spawn(Chosen.PawnClass.default.ControllerClass)); if ( NewBot != None ) InitializeBot(NewBot,BotTeam,Chosen); return NewBot; }
Very interesting... so I went to my RollerBallGame.uc file (where I specified my gametype class) and tossed it in there with some minor changes.
function Bot SpawnBot(optional string botName) { local Bot NewBot; local RosterEntry Chosen; local UnrealTeamInfo BotTeam; BotTeam = GetBotTeam(); Chosen = BotTeam.ChooseBotClass(botName); if (Chosen.PawnClass == None) Chosen.Init(); //amb log("Chose pawn class "$Chosen.PawnClass); // just to make sure I uncommented this out NewBot = Spawn(class'RBxBot'); // explicitly specify my bot class // NewBot = Bot(Spawn(Chosen.PawnClass.default.ControllerClass)); if ( NewBot != None ) InitializeBot(NewBot,BotTeam,Chosen); return NewBot; }
Compile, showdebug, viewplayer (botname), Voila!!! Controller is RBxBot! So far so good, but they arent falling down yet!...
So in the RBxBot.uc file I put in:
function SetPawnClass(string inClass, string inCharacter) { local class<RBxPawn> pClass; PawnClass = class'RBxPawn'; log("Chose pawn class "$PawnClass); // again checking to make sure it works well... PawnSetupRecord = class'xUtil'.static.FindPlayerRecord(inCharacter); PlayerReplicationInfo.SetCharacterName(inCharacter); }
There are some other advantages, since you can set bot parameters in your gameclass file. If theres an easier way, I'm all for it, but until then, I'll continue to study the bot files and start filling in the bot page as I go along...
The next item up was the Karma Ragdoll physics. I wanted to have people actually fall over when they get hit and the percentage was to be dictated by the amount of force the weapon being used would 'stun' the player. Instead of going over it here, just jump to the Karma Ragdoll page and read it. I added my working code to the bottom and tried to clear up any assumptions. I give all of the kudos to the guys who wrote that page (and the other it links from), they were essential to the learning curve.
Now its time for replication, and so far... Its a pain in the ...
Comments
Mychaeel: Welcome. But please don't put stuff of general interest on your personal page – nobody will ever find it there. Just look around a bit, find a nice place for a link to your content, add it and then create a new page to add your content from there. (If others feel that the new page should have a different name or be linked from elsewhere, no harm done – that can be changed easily.)
OBWANDO: No problem, Would a developers journal be more appropriate? Or a "Mod from nuts to bolts"? In the end I want to allow new people to look at the mod I've created from start to finish and try to give them a template for as many classes as possible with the assistance of other pages that have the definition of many of the classes. Kind of like a practical use for the instructional pages.
Foxpaw: Have you considered just using PHYS_Hovering for the skates simulation? It may not make much of a difference, but I think it behaves essentially as you have described you want your skates to act, and since it runs natively it would probrably execute faster.
LegalAssassin: Hey, Ob, are you dead? Haven't seen you around lately...