There is no spoon
Legacy:Using LocalMessages
Contents
What Are LocalMessages?[edit]
The short description would be: All the messages in the game (e.g. "You have the flag, return to base!") are LocalMessage classes. LocalMessages have two important advantages over string messages: They use less network bandwidth and they can be displayed in different languages, regardless of the language the server uses. The classes can dynamically create the displayed strings from the parameters that were passed to them.
Getting to know LocalMessages[edit]
Setting up a testing environment[edit]
To get into LocalMessages we'll try using some of the existing message classes from a simple mutator first. Make a new mutator like the one below and create an INT File for it, so it shows up in the game.
class MutLocalMessageTest extends Mutator; function ModifyPlayer(Pawn Other) { // call next mutator's ModifyPlayer, so we don't break other mutators Super.ModifyPlayer(Other); // we'll put our code here soon } // Default properties neccessary for UT2003 mutators. // Don't add this section if you're coding for earlier engine versions, // it'll only cause compiler warnings. defaultproperties { FriendlyName="LocalMessage Test" Description="Test mutator for LocalMessage classes." GroupName="LocalMessage Test" }
Now we have a testing environment we can use to play with the features of LocalMessage classes.
Broadcasting a LocalMessage[edit]
The first method to send LocalMessages we'll try is broadcasting them to all players. This sounds much more difficult than it is, because every Actor (e.g. our mutator) has a function which does exactly that.
- BroadcastLocalizedMessage (class<LocalMessage> MessageClass, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject)
The MessageClass parameter specifies the LocalMessage class that should be broadcasted and is the only required parameter. We'll get back to the optional parameters later.
Let's broadcast a simple message whenever a player respawns by adding the following code to the mutator's ModifyPlayer function:
BroadcastLocalizedMessage(class'CTFHUDMessage'); // in UT use CTFMessage2 instead
Compile the mutator, start a Practice Session/Instant Action game and add the LocalMessage Test mutator. Now every time a player or bot respawns you get the "You have the flag, return to base!" message. If you open the UnrealGame.CTFHUDMessage (or Botpack.CTFMessage2) class you'll notice that this string isn't hard-coded, but instead stored in a localized variable. Localization happens client-side, so on a client with the German version the message would appear as "Sie haben die Flagge! Zurück zur Basis!", while the French version displays "Vous avez le drapeau, regagnez la base !" even though both may be connected to a server that runs the Spanish version. (which in case of a listen server would see "Tienes la bandera, ¡regresa a la base!") :)
The Switch Parameter[edit]
The CTFHUDMessage class also contains the "The enemy has your flag, recover it!" message. To display it we have to use the first optional parameter of the BroadcastLocalizedMessage function, the Switch parameter. This is an integer value and by not specifying it the value 0 is used. Change the BroadcastLocalizedMessage call to this:
BroadcastLocalizedMessage(class'CTFHUDMessage', 1); // in UT use CTFMessage2 instead
After recompiling you now see the new message each time somebody respawns.
Most LocalMessage classes use the Switch parameter for displaying completely different messages. Some examples are the CTF messages ("Player x picked up/dropped/captures/etc. the flag") or the game status messages. ("Press FIRE to start!", "The match is about to begin...3/2/1", etc.) You could say the Switch parameter specifies the type of message that should be displayed from a given class of LocalMessages.
The Related PRIs[edit]
PRI is short for PlayerReplicationInfo. PRIs are actors holding information about a player. LocalMessages build their display strings client-side. However, due to optimization in the replication code not every bit of information about the players is sent to the clients in a network game. The things a client might want to know about even if the player isn't visible are saved in a PRI which is always available on all clients. (unlike the player's Pawn or Controller actors)
Some messages require information about one or two players. This could be a player's name (think "A's killing spree was ended by B."), a player's team or other player-specific information. The LocalMessages use their RelatedPRI_1 and RelatedPRI_2 parameters to specify up to two PRIs.
Let's try the "You were killed by ..." message. This message uses the RelatedPRI_1 parameter to get the killer's name. We will just let the respawned player be the "killer" here. Every Pawn and Controller actor has a PlayerReplicationInfo property which can be used to access the player's PRI. Change the broadcast line to this:
BroadcastLocalizedMessage(class'xVictimMessage',, Other.PlayerReplicationInfo); // use class'VictimMessage' in UT
The victim message doesn't use the Switch parameter, that's why it isn't specified here.
The Optional Object[edit]
The fourth optional parameter when broadcasting LocalMessages is of type Object. You can specify every kind of object here (Classes, Actors, Textures, Sounds, etc.), but you have to make sure that the object exists on all clients. This might only be a problem with Actors, since they have to be replicated before they can be used as the optional object parameter of a LocalMessage. In CTF and Bombing Run messages the optional object is either the flag/ball or a TeamInfo.
For this tutorial we will use the item pickup message. The pickup message is stored in the PickupMessagePlus class (same name for UT and UT2003) and only checks the Pickup class or Inventory (UT) class passed as the optional object. To get the Sniper Rifle's/Lightning Gun's pickup message we use the following line:
BroadcastLocalizedMessage(class'PickupMessagePlus',,, class'SniperRiflePickup'); // use just class'SniperRifle' in UT
This will display the message "You got the Lightning Gun." in UT2003 or "You got a Sniper Rifle." in UT.
Sending Messages To Individual Players[edit]
Sending messages to individual players is a little more complicated (but not much) than broadcasting it. All we need is a reference to a PlayerPawn in UT or a PlayerController in UT2003. The PlayerPawn and PlayerController classes have a ReceiveLocalizedMessage function which is also used by the BroadcastMessage function. BroadcastMessage calls all players' ReceiveLocalizedMessage functions to broadcast the message, we'll only call it for one player for now.
- ReceiveLocalizedMessage (class<LocalMessage> Message, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject)
As you can see ReceiveLocalizedMessage has the same parameters as BroadcastLocalizedMessage. There's no need to explain scomething fundamentally new here, so let's jump right into an example.
For this example we will use the Mutate function of our test mutator because it already gives us a reference to a player and we don't have to go look for one. First we'll extend out testing environment. Add the following code below the ModifyPlayer function or replace that function with it.
// in UT write PlayerPawn Sender instead function Mutate(string MutateString, PlayerController Sender) { // call next mutator's Mutate, so we don't break other mutators Super.Mutate(MutateString, Sender); // our code will go here }
Let's send a headshot announcement to everyone who types "mutate headshot" at the console. Add the following lines in the Mutate function:
// case-insensitive if ( MutateString ~= "headshot" ) Sender.ReceiveLocalizedMessage(class'SpecialKillMessage'); // use class'DecapitationMessage' in UT
The headshot message doesn't need any parameters, it's always the same regardless of switch, related PRIs or optional object.
Now compile the mutator and start a game with it. Whenever you type "mutate headshot" you will see (and hear) "Headshot!", and in network games only the player who types it can see and hear it.
Creating New LocalMessages[edit]
For scripters, an easy way to get into creating your own localmessages is simply to steal an existing one. I wanted some onscreen debug messages so I copied CTFHUDMessage, saved it into a file called MyScratchMessage.uc, and tweaked it to return my required messages.
class MyScratchMessage extends LocalMessage; // Switch 0: Checkreset said Die // Switch 1: Checkreset said vehicle not empty // Switch 2: Checkreset found a collidingactor required ten more seconds // Switch 3: Checkreset Destroyed! var(Message) localized string string0; var(Message) localized string string1; var(Message) localized string string2; var(Message) localized string string3; var(Message) color YellowColor; static function color GetColor( optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2 ) { return Default.YellowColor; } static function string GetString( optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject ) { if (Switch == 0) return Default.string0; else if (Switch == 1) return Default.string1; else if (Switch == 2) return Default.string2; else return Default.string3; } defaultproperties { string0="Checkreset said Die" string1="Checkreset said vehicle not empty" string2="Checkreset found a collidingactor required ten more seconds" string3="Checkreset Destroyed!" YellowColor=(G=255,R=255,A=255) bIsPartiallyUnique=True bIsConsoleMessage=False bFadeMessage=True bBeep=True Lifetime=2 DrawColor=(G=160,R=0) StackMode=SM_Down PosY=0.100000 FontSize=1 }
As a scripter, I found I could just compile that into my package and it was available using BroadcastLocalizedMessage(class'MyScratchMessage',1);
Sweavo: I don't know how to then localize this for different regions, though. I guess you need to have separate source codes that compile into classes of the same name.
Tarquin: Suppose I create and embed a LocalMessage class in MyLevel... now how do I refer to it somewhere in the map so it's saved properly? Making it placeable seems to work. :)
Related Topics[edit]
Related Classes[edit]
Category:Legacy Tutorial
Category:Legacy To Do → Add info about creating new LocalMessage classes.