Cogito, ergo sum
Legacy:Input Keys
So its time to create a class that requires the user to press a key? You've come to the right place. I'll walk you through the steps here.
For UT2003 and newer, see interaction.
Contents
Step 1: Setting up the Key Binding[edit]
This is the easiest step. Start by creating a new class, something like ExtraWordBindings (please replace Word with something descriptive, thanks!) and make it look something like this:
Class ExtraWordBindings extends UTExtraKeyBindings; defaultproperties { SectionName="YourSection" // Enter a descriptive section name here LabelList(0)="First Binding Caption" // Something Descriptive, Please. LabelList(1)="Second Binding Caption" AliasNames(0)="mutate Word" // Change Word to something that describes the action this key will do AliasNames(1)="mutate Word" }
Compile the script and create an entry in the [Public] section of your package's int file:
Object=(Name=MyPackage.MyKeyBindingsClass,Class=Class,MetaClass=UTMenu.UTExtraKeyBindings)
Now you should have a working option in the "Controls" option panel.
Step 2: Creating the mutator to process your key input[edit]
Okay now you can use one mutator to parse all your key bindings, or one mutator for each binding. I like one mutator for each binding, because these mutators should be automatically added into the mutatorlist if the associated actor is present in the map. DO NOT CREATE AN INT ENTRY FOR THESE MUTATORS!
They should look something like this. (Once again, PLEASE replace "Word" with something DESCRIPTIVE!)
class WordKeyMut extends Mutator; function AddMutator(Mutator M) { if ( M.IsA('WordKeyMut') ) // This mutator's name! { return; //only allow one mutator } Super.AddMutator(M); } function Mutate(string MutateString, PlayerPawn Sender) { local YourClass Other; // YourClass will be the name of the class that we // will create in the next step - the actual actor // that this mutator is processing key input for. if (MutateString ~= "SomeString") // SomeString should be equal to an AliasName set in the class above, //without "mutate". i.e., if your AliasName was "mutate Activate", //SomeString should be "Activate" { foreach AllActors(class'YourClass', Other) { if ( Other.IsA('YourClass') ) { log("YourClass Detected"); if ( Other.BooleanValue ) // Change BooleanValue to a descriptive variable name! // It will be set in the class we will create in the next // step, to see if the actor is actually in a state that // it can be activated. { log("YourClass Active"); Other.FunctionName( ); // Change FunctionName to something descriptive! // It will be created in the next step as well, // and it will contain everything we want to happen //when the user presses a key. } } } } if ( NextMutator != None ) NextMutator.Mutate(MutateString, Sender); }
Step 3: Creating your actor![edit]
Create your map actor as you otherwise would create any other map actor. How to create them is beyond the scope of this article. Once its created, you want to add the following near the beginning of the class:
function PreBeginPlay() { Level.Game.BaseMutator.AddMutator(Spawn(class'Package.Mutator')); // Replace Package with your package name, // and Mutator with the name of the mutator // we created above. Super.PreBeginPlay(); }
Right at the very beginning, define a boolean variable called whatever you replaced "BooleanValue" with in the class above. You may also want another variable (as an Actor), to store who is allowed to actually activate the object.
Now you code a function with its name set to whatever you made "FunctionName", which will be what you want to happen when a player activates the object. You will also need to put in the code to set your boolean to true when someone is allowed to activate it.
Now you're set! Below is an example of what I've done, to create a trigger that requires the user to activate it before use.
My Working (we hope) Example![edit]
Here is the trigger class, it works like any other trigger:
// // UseTrigger: Almost identical to Trigger except requires user to press // activate to spawn the event. // By Nick Bowler (Draconx), http://www.deadjunkie.com/~draconx/ // class UseTrigger extends Trigger; //----------------------------------------------------------------------------- var bool Activatable; var Actor ActivatedBy; var Actor OtherBkp; // function PreBeginPlay() { Level.Game.BaseMutator.AddMutator(Spawn(class'Package.ActivateMut')); Super.PreBeginPlay(); } // // Called when something touches the trigger. // function Touch( actor Other ) { if (Other.IsA('PlayerPawn')) { log("UseTrigger Activated"); OtherBkp = Other; Activatable = true; } } function UsePressed( ) { log("UseTrigger Used"); Super.Touch( OtherBkp); } // // When something untouches the trigger. // function UnTouch( actor Other ) { if (Other.IsA('PlayerPawn')) { log("UseTrigger Deactivated"); Activatable = false; } } defaultproperties { Activatable=false }
Here is the mutator which processes the key input:
// // ActivateMut - Required for UseTrigger to function correctly. Placing a // UseTrigger in your map automatically adds this mutator, so it should not // be added manually. // By Nick Bowler (Draconx), http://www.deadjunkie.com/~draconx/ // class ActivateMut extends Mutator; function AddMutator(Mutator M) { if ( M.IsA('ActivateMut') ) { return; //only allow one mutator } Super.AddMutator(M); } function Mutate(string MutateString, PlayerPawn Sender) { local UseTrigger Other; if (MutateString ~= "activate") { foreach AllActors(class'UseTrigger', Other) { if ( Other.IsA('UseTrigger') ) { log("UseTrigger Detected"); if ( Other.Activatable ) { log("UseTrigger Active"); Other.UsePressed( ); } } } } if ( NextMutator != None ) NextMutator.Mutate(MutateString, Sender); }
And here is the class which adds to the keybindings:
class UseTriggerBindings extends UTExtraKeyBindings; defaultproperties { SectionName="Miscellaneous" LabelList(0)="Activate" AliasNames(0)="mutate activate" }
And there you have it. I hope you found this tutorial useful!
Foxpaw: This is a clever workaround, but wouldn't exec functions/input variables be more elegant and easier, too?
Wormbo: Exec functions can only be executed by the player who is the owner of the actor and I don't see how input variables would work in this case.
Foxpaw: Don't mutators have full access to exec functions and input variables for the local viewport?
Wormbo: No, mutators are serverside. They only have a Mutate function which is called by the PlayerController's exec function Mutate which in turn is always replicated to the server.
Foxpaw: Oh. I thought mutators were loaded on both the client and the server and followed the normal replication rules.
Wormbo: They follow replication rules, that's why they aren't replicated at all unless you change RemoteRole, bAlwaysRelevant, etc.
ToJo: Does anyone know if there is a simplier way to do this in UT 2003?
Foxpaw: An interaction should work.
Draconx: UT 2003 already has a UseTrigger, it works just like mine (unfortunately, the Activate key doesnt seem to appear in the normal key bindings screen, and must be set manually.
MythOpus: Are you refering to the Activate key for the UseTrigger? That is set to the Use key which is by default 'U'...
Draconx: Yeah but the control doesnt appear on the controls screen ingame. I used the rawkeybindings thing to change it on my own install.
Xian: Interesting concept. But this is buggy, as if we have 2 PlayerPawns activating 2 separate Triggers, we'd have both of them activatable. This in turn would make the foreach iterator execute even the trigger which the Player running the command isn't touching. i'd say this would be a better workaround:
function Touch( actor Other ) { if (Other.IsA('PlayerPawn')) { log("UseTrigger Activated"); PlayerPawn(Other).HitActor = Self; OtherBkp = Other; Activatable = true; } } function UnTouch( actor Other ) { if (Other.IsA('PlayerPawn')) { log("UseTrigger Deactivated"); Activatable = false; PlayerPawn(Other).HitActor = None; } }
... and...
if (MutateString ~= "activate") { if ((Sender.HitActor != None) && Sender.HitActor.IsA('UseTrigger')) { log("UseTrigger Detected"); if ( UseTrigger(Sender.HitActor).Activatable ) { log("UseTrigger Active"); UseTrigger(Sender.HitActor).UsePressed( ); } } }