Simple Notification Mod

Ever wanted to notify everyone that someone has entered a certain room? I did, when editting the DM-GoreCage level. The idea for this level was to create a room you can enter from different ways, but where the only exit was in death. It would be nice if people actually knew you were in that room so you won’t hang around there ’till someone accidentally passed by. So I created the NotifyTrigger. This tutorial will explain exactly how and why I did this.

Assumptions

I assume you already know how to create a simple level in UnrealEd, and that you understand the structure of deriving classes and overriding functions. If not, go to the Documentation section and read UnrealScript OOP.

The Notifier Trigger

When I want to create some sort of effect, I first look for the class which creates almost the effect I have in mind. The effect I want to create (if you don’t know what I’m talking about, read ‘For short’ at the top of this document) mimics the function of a trigger. A simple trigger already has a Message-property which will display the message on the screen of the guy triggering the trigger. What I had to do then, was to find the function which displays this message, and add a little code of my own that displays this message on everybody’s screen.

First of all, find the Trigger-class. On the right of the UnrealEd screen you’ll have a bar called ‘Browse’. Right now, it will probably show a list of textures. Select ‘Classes’ from the listbox at the top. Click the - in front of ‘Triggers’, and double-click on Trigger. Now a blue edit box will pop up, showing the code used to create the generic trigger.

This piece of code tells Unreal(Ed) the variable Message is a string, and should be shown in the properties-menu of this class:

// Human readable triggering message.
var() localized string Message;

So to find the function which displays the message on you screen, we have to find the function which uses this variable. This is done in the function Touch:

//
// Called when something touches the trigger.
//
function Touch( actor Other )
{
    local actor A;
    if( IsRelevant( Other ) )
    {
        if ( ReTriggerDelay > 0 )
        {
            if ( Level.TimeSeconds - TriggerTime < ReTriggerDelay )
                return;
            TriggerTime = Level.TimeSeconds;
        }
        // Broadcast the Trigger message to all matching actors.
        if( Event != '' )
            foreach AllActors( class 'Actor', A, Event )
                A.Trigger( Other, Other.Instigator );

        if ( Other.IsA('Pawn') && (Pawn(Other).SpecialGoal == self) )
            Pawn(Other).SpecialGoal = None;

        if( Message != "" )                            // <--
            // Send a string message to the toucher.   // <--
            Other.Instigator.ClientMessage( Message ); // <--

        if( bTriggerOnceOnly )
            // Ignore future touches.
            SetCollision(False);
        else if ( RepeatTriggerTime > 0 )
            SetTimer(RepeatTriggerTime, false);
    }
}

The highlighted lines are the ones that display the message. These lines are in the Touch-function, so we have to override Touch(). This is how I did it:

//===================================================
// NotifyTrigger.
//===================================================
class NotifyTrigger expands Trigger;

var(Trigger) string NotifyMessage;

function Touch( actor Other )
{
    local PlayerPawn A;
    Super.Touch(Other);

    if(NotifyMessage != "" && Other.IsA('Pawn'))
        foreach AllActors( class 'PlayerPawn', A, Event )
            A.ClientMessage(NotifyMessage);
}

The top three lines are comment, and the fourth line tells Unreal that NotifyTrigger is a child of the Trigger-class.

I wanted the possibility to use the original Message-property the same way a normal trigger uses it. Therefore I used a new property for the message that will be shown on everybody’s screen. By default, properties you create show up in the property-box in their own section, named after the object. I did not want a NotifyTrigger section with just one property, so I used var(Trigger) instead of var().

I wanted to add a new functionality to the Touch()-function, so I made a new Touch()-function (line nr. 8), and called the original Touch() function (line nr. 12).

Line nr. 14 will make sure the NotifyMessage is sent to all players if there actually is a message, and if it was a pawn (a player or bot) that triggered the trigger.

Lines 15 and 16 will iterate through all the players, and send them the NotifyMessage. This is the actual core of the program.

Speed Improvement

This was originally where the tutorial ended. After its creation I read something on the speed of UScript. It seems that the usage of foreach AllActors is quite slow, because it iterates ALL actors. Since I just wanted to send a message to all players, and not to any other actor, there is a much faster way.

The LevelInfo object contains a property, called PawnList. Since every pawn has a property nextPawn, it is possible to iterate through the players, without having to look at all the other actors.

Here is an example, which can be used instead of line 15 and 16 of the above code:

for( P=Level.PawnList; P!=None; P=P.nextPawn )
    P.ClientMessage(Msg);

If you want the players to hear a beep when receiving the message, change the second line to:

P.ClientMessage(Msg,,true);

The second argument of the ClientMessage call is skipped. It is the (optional) type of the message. An empty type will simply put the message on the screen of the player. If you put ‘Say’ or ‘TeamSay’ here, the message will contain your name and current location. The third argument ’true’ indicates that is should play a beep.

Thanks to Anthony Bowyer-Lowe ( ynohtna@ynohtna.org) for this info.

You can also use the BroadcastMessage function, located in the Actor-class. You can call it like this: BroadcastMessage("Your message here",True). It will do the same as the above, but it will only send a message if it is allowed according to the game rules defined in the GameType class.

Conclusion

I hope this tutorial has cleared some things up for you. If you do not fully understand anything I’ve said here, don’t hesitate to contact me!