Gah - a solution with more questions. – EntropicLqd
Legacy:Trystan/11 16 02b
11/16/02 Breaking down Object.uc.
EDIT: Most of the information herein is referenced in Global Function. If you think I'm wrong about something or misinformed, let me know. :)
Object.uc is the base class for everything within UnrealScript. I'm going to attempt to break it down and see what parts of it do what. This won't be comprehensive as I'm sure I won't understand it completely right now, but it'll be a good start. Wiki's Object listing is of course available, but it doesn't go into specifics. (Is it supposed to?) Why explain it here? Why not. Putting my thoughts into words has always been the best way for me to ensure I understand something; it forces you to swallow knowledge and regurgitate in your own vocabulary. Unless you repeat the definition word for word understanding the subject becomes very important. The point of this exercise is, ultimately, to become familiar with how weapons are added and/or modified in Unreal Tournament 2003.
Object is the base of everything. It has variables defined to allow for server side or client side use only (RF_NotForClient and RF_NotForServer) of objects; flags to keep objects from being loaded in the editor (RF_NotForEdit); flags to indicate whether an object is private or public (RF_Public); and flags to indicate that the object supports undo and redo in the editor (RF_Transactional) and whether or not the object should be saved and loaded (RF_Transient). These flags are applied to Object.ObjectFlags to take effect. From an UnrealEd dump of the classes (not the Epic source) these flags are located on lines 22 - 30.
// Object flags. const RF_Transactional = 0x00000001; // Supports editor undo/redo. const RF_Public = 0x00000004; // Can be referenced by external package files. const RF_Transient = 0x00004000; // Can't be saved or loaded. const RF_Standalone = 0x00080000; // Keep object around for editing even if unreferenced. const RF_NotForClient = 0x00100000; // Don't load for game client. const RF_NotForServer = 0x00200000; // Don't load for game server. const RF_NotForEdit = 0x00400000; // Don't load for editor.
Most of the information I gathered was quite literally taken from the comments. This is the way almost every Epic created uc file is. If you read it, you'll generally find your way around it.
Object is the home of the Guid struct. A Guid is used by many pieces of software (Windows, Mac OSX, and Linux all use them in one way or another) to help indicate a unique version of an object. They are usually randomly generated, 64 bit or larger numbers that make absolutely no sense to a human but can be used by a computer to ensure that it is using the correct version of your object. This struct is defined as
// A globally unique identifier. struct Guid { var int A, B, C, D; };
There's support for a quite large GUID built into the base class. It is my understanding that Unreal literally uses the GUID inside an object to ensure that the correct object is being used; that is, someone didn't go in and change your code and replace it with a trojan or cheat and then rebuild it. I haven't researched the full usage of the GUID as of yet; when I do I'll ensure I update this page to reflect it.
The Object class includes support for many of the basic variable types and functions. Vector, Plane, Rotator, Coords, Quat, Range, RangeVector, Scale, Color, Box, IntBox, FloatBox, BoundingVolume, Matrix, InterpCurvePoint, InterpCurve, EDrawPivot, and Compressed Position are all defined inside Object. The maximum integer value and value of Pi are also defined herein as constants.
Now we get to the heart of the Object class.
// Bool operators. native(129) static final preoperator bool ! ( bool A ); native(242) static final operator(24) bool == ( bool A, bool B ); native(243) static final operator(26) bool != ( bool A, bool B ); native(130) static final operator(30) bool && ( bool A, skip bool B ); native(131) static final operator(30) bool ^^ ( bool A, bool B ); native(132) static final operator(32) bool || ( bool A, skip bool B );
That's a small sampling of what you'll find within the class. I won't reproduce it in whole here because it would be pointless. To understand what's going on inside Object at this point in time you have to understand a bit about C++.
First off, native means that the code is actually defined within C++ and not UnrealScript. Considering how crucial speed would be for the base class of Object this makes great sense. The anticipated code execution difference between C++ and UnrealScript is that C++ will execute roughly 20 times faster than the equivalent UnrealScript code. The above lines are defining what function should be called when the operator is used against the variables. Let's take one line apart and explain what I mean.
native(242) static final operator(24) bool == ( bool A, bool B );
Native is explained above. I'm not exactly sure what (242) is although I wager it's an array reference of some sort. Static means that the class that implements this function only implements it once; it does not require an instantiation of the class in order to be called. (This is in C++, not UnrealScript.) Final means that classes that derive from this object cannot override this method with their own. (Compare it to a "sealed" class in C++.) My best guess at what operator means is that it is telling the compiler that this function defines exactly that: a logical operator of some sort. bool is the return value. == is the operator being defined. ( bool A, bool B ) defines the arguments that this operator takes.
To understand how this all works you'll have to understand operator overloading in C++. If you write the following code:
var() bool A, B; A = false; B = true; return A == B;
as compared to
var() int A, B; A = 1; B = 2; return A == B;
how does the compiler know which function to properly call? It looks at what's called function signatures. Compilers have gotten much smarter through the years and C++ takes full advantage of that. In the case of the first section of code the compiler sees == (bool, bool) and calls the appropriate function. In the second case it sees == (int, int) and will call the appropriate integer comparison function rather than the boolean comparison function.
I doubt that many of us will ever feel a need to write our own operators. The operators built into UnrealScript are quite complete - simply open Object.uc and take a look. However it's nice to understand how these operators are defined because if you're curious whether or not something can be bit shifted, or tested for equality, a quick peek into Object.uc should give you the answer you're looking for.
Now that we've defined all of the operators that UnrealScript supports we move on.
// Logging. native(231) final static function Log( coerce string S, optional name Tag ); native(232) final static function Warn( coerce string S ); native static function string Localize( string SectionName, string KeyName, string PackageName );
Log writes an entry to the log and is useful for debugging purposes. Interested in whether or not your bullet is actually slowing down? Have it log messages. Although this is a native function it does require (I believe) drive access and thus is fairly costly. You should only log during development IMHO, and only where necessary. I believe Warn is identical to log except for the severity level of it; I could not find any information on how Warn and Log differed. (Possibly Warn prints to console a warning? Or perhaps to screen? I'll have to play with this later and see what the exact difference is.) Localization isn't supported as of yet inside Unreal 2K3 so I wager the last function is a placeholder for when it is. (From Wiki Global Function warn is the same as log, it simply prefixes what it writes to the log with "ScriptWarning:" and contains various information about the object that it was called with. Handy!)
// Goto state and label. native(113) final function GotoState( optional name NewState, optional name Label ); native(281) final function bool IsInState( name TestState ); native(284) final function name GetStateName();
Next up! State management! This is an entire subject and I've got a lot to learn about it. For now check out the Wiki page at State. State management is incredibly important to any total conversion and I know I've got to master it. :)
// Objects. native(258) static final function bool ClassIsChildOf( class TestClass, class ParentClass ); native(303) final function bool IsA( name ClassName );
These are interesting functions. IsA() doesn't require an object of the class to be tested; it only requires the string name of the class. They can be used to determine effects if and only if the objects being referenced are of the right class. Perhaps you wish grenades to explode harmlessly in water; you could use the IsA() test to see if an exploding object is of the class you wish to disable underwater. Etc., etc. These two functions are usually quite popular and used a lot in the sample code I've seen. ClassIsChildOf requires objects of the classes to be tested. (More information is at Global Function.)
// Probe messages. native(117) final function Enable( name ProbeFunc ); native(118) final function Disable( name ProbeFunc );
These reference Probe. Basically a ProbeFunc is one that you can enable or disable based on the state of an object. This is awesomely useful if you only wish ExplodeOnImpact to be enabled if AltFire was used to fire the projectile, or any other conditional functions. IsA('Grenade') and in water can be used to disable GrenadeExplode; etc. These functions are what are used to enable and disable said probe functions.
// Properties. native final function string GetPropertyText( string PropName ); native final function bool SetPropertyText( string PropName, string PropValue ); native static final function name GetEnum( object E, int i ); native static final function object DynamicLoadObject( string ObjectName, class ObjectClass, optional bool MayFail ); native static final function object FindObject( string ObjectName, class ObjectClass );
Properties are used on an object to track information. GetPropertyText can be used to retrieve a previously SetPropertyText. They're generic functions used to, again, simply track information. This property information can then be referenced by any function that has a reference to the object itself: there's no need to pass variables about.
The last three I'm not sure what they do. GetEnum sounds like it retrieves an enumeration of some sort. How, or why, I don't know. Let's do a search through the existing code and see what we come up with. GetEnum doesn't seem to exist or be called outside of the Object.uc class. Interesting.. What does Wiki say about GetEnum? See Global Function, or..
- name GetEnum (Object E, int i) [static]
- Returns the i-th element of the given enumeration, as in GetEnum(enum'MyEnum', 2) or an empty name of the specified index exceeds the number of elements in the enumeration.
So you could use GetEnum as an indexer to a list of enumerations so you could possibly attach states or other information to a generic indicator.
DynamicLoadObject does what it says. If MayFail is set to true no logging will occur if the object load fails. In our mod we'll probably end up using this to summon items like sandbags, etc. I imagine FindObject finds an instance of an object but no information is currently available in Wiki and context determination has failed in UT2K3 source. Another one to play with..
// Configuration. native(536) final function SaveConfig(); native static final function StaticSaveConfig(); native static final function ResetConfig();
These are used to retain information about an object's state. Not a lot of information is available about these either. SaveConfig() and StaticSaveConfig() have examples spread throughout the code; ResetConfig() is only defined but never used.
// Return a random number within the given range. final function float RandRange( float Min, float Max ) { return Min + (Max - Min) * FRand(); }
Returns a random number between min and max. Self explanatory to me.
native(535) static final function StopWatch(optional bool bStop); //amb: for script timing native final function bool IsOnConsole(); // for console specific stuff native final function bool IsSoaking();
StopWatch is used for timing script code and seems to be available in UT2K3 only. IsOnConsole() is used to determine whether or not the mod is running on the XBox (or future) consoles. Since I don't know of a way to get a mod onto the XBox at this point in time I think it's fairly safe to ignore this. The only reference to IsOnConsole() in code is the following code fragment from PlayerController.uc:
exec function InvertLook() { local bool result; result = PlayerInput.InvertLook(); if (IsOnConsole()) { class'XBoxPlayerInput'.default.bInvertVLook = result; class'XBoxPlayerInput'.static.StaticSaveConfig(); } }
IsSoaking() also seems to be console specific. It's referenced in Console.KeyEvent as
if( Action == IST_Press ) { TimeIdle = 0; if( bRunningDemo && !IsSoaking() ) { StopRollingDemo(); return( true ); } }
and in Console.Initialized as
event Initialized() { if( IsSoaking() ) { TimePerTitle = 1; TimePerDemo = TimePerSoak; } }
Again it seems fairly safe to ignore IsSoaking() unless you're doing console programming.
event BeginState(); event EndState(); event Created(); //amb: notifiction for object based classes only (not actors) native(197) final iterator function AllObjects(class baseClass, out Object obj); //amb
BeginState() is called when a state change is initiated, but prefer the state change is initiated. EndState() is called prior to exiting the current state. Both of these functions are meant to be overriden and made custom for your class. The default definition in Object does nothing.
Created() is another function that can be overridden to perform custom tasks when an object is created. The default definition does nothing.
AllObjects() is an iterator written that can be used to cycle through each and every object in game. A costly, but in some rare cases absolutely necessary, function. Although this isn't the best idea, consider if you had to find and remove every bullet in the game (perhaps a special power was used). You could call AllObjects and check each object returned against IsA('Bullet') and perform actions appropriately.
I understand that a great deal of this information is a simple rehash of Global Function and Object but it helps me to understand things when I'm forced to evaluate them myself. As well others may find the little extra I added enough for them to understand. Dunno.
Next up is Actor.uc, then Inventory.uc, and finally Weapon.uc, the heart of the matter. Once I reach Weapon.uc I'll be examining the best way I've found to add weapons and change things. It's going to be a fun ride.
Return to Trystan/Developer Journal.