FAQ / issues
changelog
developers
history
contact
game-monitor.com

This week's tutorials focuses on Text Displays. Aeron's tutorial last week brushed upon this last week. This week I intend to make it clear about the system of text displays, and show their flexibility.

So what exactly is the script? It's a portion (only a portion, we may show the rest of the script at a later stage) of my Unreal Tournament script. It basically copies all our favourite messages that would appear on screen when events such as killing enemies happened.

Forum support thread: forum.mtavc.com Please ask your questions in this forum thread.

script5small.png

Video footage: Video footage: scriptvideo5.wmv - WMV9 codec (640x448, 30fps)

UT Script: script5a.lua
Kill messages: script5b.lua

Preparation

addEventHandler ( "onPlayerSpawn", getRootElement(), "utSpawnClearSpree" )
function utSpawnClearSpree ( sp, team )
	setElementData ( source, "currentSpeedKill", 0 )
	setElementData ( source, "killingSpree", 0 )
end
Firstly, we setup an event handler to launch onPlayerSpawn. You should be very familiar with event handlers at this stage.

When a player spawns, we set some values that are stored to a player to 0. These are * currentSpeedKill - The amount of kills the player has done quickly. This allows for "Double kill" and "Multi Kill" etc messages * killingSpree - The number of kills a player has done without dying.

As these both will always be 0 when a player dies/respawns, they are set to 0 here.

Next, we repeat the process for when the resource is loaded

addEventHandler ( "onResourceStart", getRootElement(), "utLoadClearSpree" )
function utLoadClearSpree ( sp, team )
	for k,v in getElementsByType ( "player" ) do
		setElementData ( source, "currentSpeedKill", 0 )
		setElementData ( source, "killingSpree", 0 )
	end
end
When the resource starts, we loop through every player in the server (this has been covered in one of the earlier tutorials) and also set all their spree details to 0.

Setting up static displays Static displays? What the hell are they? Well, this is text that does not need to change on the fly. For example, when a player achieves a double kill, it will display "Double kill!" on his screen. This text never needs to change, it will always display this same text whenever needed.

addEventHandler ( "onResourceStart", getRootElement(), "utLoad" )
function utLoad ( name )
Notice I've used another event handler for an event that has already been used. Why have I done this? It allows for tidier code - i can keep seperate sections for each particular part of the code, which makes multiple event handlers extremely useful.

Back on topic, I'll need to introduce MTA's text display system and how it works.

The MTA text display system has two main parts - text displays and text items.

A text item is a piece of text - where you enter your text, colour, size, etc.

A text display can be thought of as a group of text items. You can't physically see a text display, only the text items attached to it.

This system is very useful as it allows lots of text to be displayed simultaneously within a group (i.e. the text display). You can create as many text displays as you like and hide/show them upon will. It should be made clear that a text item is useless if it is not attached to a text display - there is no way to display a text item on its own, it must be part of a text display.

So first, let's create our text displays:

	---create our text displays
	staticDoubleKill = textCreateDisplay ()
	staticMultiKill = textCreateDisplay ()
	staticUltraKill = textCreateDisplay ()
	staticMonsterKill = textCreateDisplay ()
	
	staticHeadshot = textCreateDisplay ()
I've created 5 displays, one for each piece of text. This means i can show/hide them individually, so they each need their own display.

Next, I create my first text item:

	local txt_staticDoubleKill = textCreateTextItem ( "Double Kill!", 0.5, 0.3, "medium", 255, 0, 0, 255, 1.7 ) --create the text 
The arguments are, ( text, position x, position y, priority, red, green, blue, alpha, scale )

This means they can be placed anywhere on the screen, with any colour and any size. The "alpha" argument allows for transparancy. The "priority" argument is the rate at which the text item needs to be updated. In most cases it is safe to set this to "medium".

Next we need to attach this text item to the appropriate text display:

	textDisplayAddText ( staticDoubleKill, txt_staticDoubleKill ) --add the text to our staticDoubleKill display 
The textDisplayAddText allows attachment of a text item to a text display. The arguments are self-explanatory.

We then repeat the process for all other text items:

	local txt_staticMultiKill = textCreateTextItem ( "Multi Kill!", 0.5, 0.3, "medium", 255, 0, 0, 255, 1.9 ) --create the text
	textDisplayAddText ( staticMultiKill, txt_staticMultiKill ) --same process, add the text to our staticMultiKill display
	--Ultra kill
	local txt_staticUltraKill = textCreateTextItem ( "ULTRA KILL!!", 0.5, 0.3, "medium", 255, 0, 0, 255, 1.9 )
	textDisplayAddText ( staticUltraKill, txt_staticUltraKill )
	--Monster kill
	local txt_staticMonsterKill = textCreateTextItem ( "M O N S T E R  K I L L !!!", 0.5, 0.3, "medium", 255, 0, 0, 255, 1.9 )
	textDisplayAddText ( staticMonsterKill, txt_staticMonsterKill )
	
	--Headshot
	local txt_staticHeadshot = textCreateTextItem ( "Head Shot!!", 0.5, 0.204, "medium", 255, 0, 0, 255, 1.5 )
	textDisplayAddText ( staticHeadshot, txt_staticHeadshot )
end

Now we've set up all our static text displays - we just need to trigger them

Triggering static text displays

addEventHandler ( "onPlayerWasted", getRootElement(), "utPlayerWastedShowStaticText" )
function utPlayerWastedShowStaticText ( totalammo, killer, killerweapon, bodypart )
	if getElementData ( killer, "currentSpeedKillTimer" ) ~= nil then --check if there's a timer stored
		killTimer ( getElementData ( killer, "currentSpeedKillTimer" ) ) --destroy it if there is
	end
First, we have the onPlayerWasted event. The passed variables are fairly self-explanatory.

This part checks if there's a timer stored in a player's element data, and kills it if so. This will be explained in more detail a little later.

	if bodypart == 6 or bodypart == 5 then --if the bodypart is the id of the head
		textDisplayAddObserver ( staticHeadshot, killer ) --display the static headshot
		setTimer ( "textDisplayRemoveObserver", 3000, 1, staticHeadshot, killer ) --and remove it 3 seconds later
	end
Next, we take advantage of the bodypart variable. This is extremely useful in detecting where a player was killed when he was shot. In this case, we want to know if it was the head - IDs 5 and 6.

If it was the head, then we use the textDisplayAddObserver function. This basically allows showing of a text display to a certain person. In this case, we display the staticHeadshot text display to the killer.

We then set a timer for 3 seconds to remove the text display from the killer's screen

Next we set up our speed kills code:

	if ( killer ) and ( killer ~= source ) then --if there is a killer, and the killer is not the same person who died (i.e. he killed himself)
		local currentSpeedKills = getElementData ( killer, "currentSpeedKill" ) --get the number of speed kills he's done
		setElementData ( killer, "currentSpeedKill", currentSpeedKills + 1 ) --and increase it by one 
We use an if statement to make sure there is a killer, and that the killer is not the person who died.

We first use getElementData to get his current amount of speed kills, (remember this is set to 0 by default), then we set the new one to currentSpeedKills + 1, as the killer has just made a new kill

		local returnTimer = setTimer ( "setElementData", 3000, 1, killer, "currentSpeedKill", 0 ) --restore it to 0 in three seconds
		setElementData ( killer, "currentSpeedKillTimer", returnTimer ) --store this timer, so it can be killed later 

This part relates to killing the timer earlier. Basically, since it is a speed kill, the number of consecutive kills must return to 0 after 3 seconds. Therefore a timer is set to 0 after 3 seconds. This timer is then stored in element data of the player. This means next time onPlayerWasted is called, the timer to return it to 0 can be killed to prevent it interfering.

		showSpeedKill ( killer ) --initiate the showSpeedKill function
	end
end
We then call a custom showSpeedKill function:

function showSpeedKill ( player )
	local currentSpeedKills = getElementData ( player, "currentSpeedKill" )
	local display
	if currentSpeedKills >= 2 then --if the amount of speed kills is greater than 2
		--then we'll need to create an announcement for multiple kills
		--therefore remove any previous text displays of these announements to prevent overlap
		textDisplayRemoveObserver ( staticDoubleKill, player )
		textDisplayRemoveObserver ( staticMultiKill, player )
		textDisplayRemoveObserver ( staticUltraKill, player )
		textDisplayRemoveObserver ( staticMonsterKill, player )
Here we basically check the amount of speed kills by checking element data. If it is greater or equal to 2, then the person must have at least achieved a double kill. So first we remove any other old text displays that may be laying around, shown with all the textDisplayRemoveObserver functions.

We then kill any timers that may be around to destroy the text by checking element data (see a little further below)

		--kill the timer that was supposed to remove one of those text displays
		killTimer ( getElementData ( player, "killDisplayTimer" ) )

Next we follow a similar process with displaying "Headshot!!" earlier:

	 		--Setting the display depending on the number of kills
		if currentSpeedKills  == 2 then display = staticDoubleKill
		elseif currentSpeedKills == 3 then display = staticMultiKill
		elseif currentSpeedKills == 4 then display = staticUltraKill
		elseif currentSpeedKills >= 5 then display = staticMonsterKill
		end</p>
 
<p>--Display it
		textDisplayAddObserver ( display, player )
		--Remove the text display after 3 seconds
		local killDisplayTimer = setTimer ( "textDisplayRemoveObserver", 3000, 1, display, player )
If the currentSpeedKills is 2 then the variable "display" is defined as staticDoubleKill. We then add observer according to the required text display. If the kill is 3, 4 or 5 speed kills we define a different text display. This variable is then used in the textDisplayAddObserver function.

Note that Monster kill is >=5 - this means it repeats for any value greater than or equal to 5

Finally, like with the headshot message, we set a timer to remove the message after 3 seconds.

Setting up dynamic displays As the name suggests - these are the opposite of static text displays. They will need to change according to the situation.

So let's first off set everything up onResourceLoad:

addEventHandler ( "onResourceStart", getRootElement(), "utStartCreateDynamic" )
function utStartCreateDynamic ( name )
	--killing spree text (e.g. "x is on a killing spree!") is global.  Therefore only one text display is required but everyone must be an observer.
	local dynamicKillingSpree = textCreateDisplay ()
	killingSpreeText = textCreateTextItem ( "", 0.5, 0.271, "medium", 0, 128, 255, 255, 1.5 )
	killingSpreeTimer = false
	textDisplayAddText ( dynamicKillingSpree, killingSpreeText )
As before, we create a text display, and define it as dynamicKillingSpree. We then create a text item. But notice that the text in this item is blank. Why is this? The text is going to change on the fly whenever needed. For now its blank, but it will change when text is to be shown. This means that we do not need to show or hide any observers - everyone can be observers but the text can be blank. We define a blank global "killingSpreeTimer" variable - this will be shown a little later (but given the past timers, you should be able to guess what this will do!) We then add the text to the textdisplay.

Next we do a loop of every player in the server:

	
	for k,v in getElementsByType ( "player" ) do
By now, this should be very familiar to you. If not, check up on some of the previous tutorials.

So why are we looping through players? We need to create a text display unique to every player, as that player will only see his or her designated messages. So we loop through every player in the server and create a text display for them each. The reason we did not do this for 'dynamicKillingSpree' is because this refers to the "Talidan is on a killing spree!" message that appears for every player, so only one text display is required.

		--All these displays require one per player, not global.  Therefore they're created on a per-player basis.
		local dynamicTextDisplay = textCreateDisplay () --create a single display to handle all dynamic text
		textDisplayAddObserver ( dynamicTextDisplay, v )
		--next we create a set of blank text items for this single display
		local youWereKilledText = textCreateTextItem ( "", 0.5, 0.335, "medium", 255, 0, 0, 255, 1.5 ) --for "You were killed by x"
		local youKilledText = textCreateTextItem ( "", 0.5, 0.237, "medium", 0, 128, 255, 255, 1.5 ) --for "You killed x"
		local weaponPickup = textCreateTextItem ( "", 0.5, 0.9, "medium", 255, 255, 255, 255, 1.5 ) --for "You picked up x ammo"
		--add all these text items to the display
		textDisplayAddText ( dynamicTextDisplay, youWereKilledText )
		textDisplayAddText ( dynamicTextDisplay, youKilledText )
		textDisplayAddText ( dynamicTextDisplay, weaponPickup )
		--store them as element data to the player, so they can be retrieved later 
Here we create one text display, and add three text items to this. We dont need individual text displays for each text item, as they will always be an observer of the text. As i mentioned earlier, we can change whether they see the text by making the text blank.

So we create 3 text items, i'll quickly explain which each of these refer to: * youWereKilledText - This refers to the text that that displays "You were killed by Talidan" which appears on your screen when you are killed * youKilledText - This refers to the text when YOU kill another player - "You killed Talidan" * weaponPickup - This refers to the text when you pick up a pickup - e.g. "You picked up an M4"

Next we have to attach these individual displays to the player so they can be retrieved later. As usual this is done via element data:

		setElementData ( v, "textItem_youWereKilledText", youWereKilledText )
		setElementData ( v, "textItem_youKilledText", youKilledText )
		setElementData ( v, "textItem_weaponPickup", weaponPickup )
This should be pretty self explanatory at this stage.

Then:

		textDisplayAddObserver ( dynamicKillingSpree, v )--make everyone an observer of the killing spree text(see above)
	end
end
This makes everyone an observer of the dynamicKillingSpree text display. Since the text display is global, we only need one text display. We still however require everyone to see that text display, hence add this function as part of the loop of every player.

The next part is a duplicate of the one above, only designed for players who are joining the server and are not around when the resource is started:

--here we repeat the process for a player who joins
addEventHandler ( "onPlayerJoin", getRootElement(), "utJoinCreateDynamic" )
function utJoinCreateDynamic ()
	local dynamicTextDisplay = textCreateDisplay () 
	textDisplayAddObserver ( dynamicTextDisplay, source )
	local youWereKilledText = textCreateTextItem ( "", 0.5, 0.335, "medium", 255, 0, 0, 255, 1.5 )
	local youKilledText = textCreateTextItem ( "", 0.5, 0.237, "medium", 0, 128, 255, 255, 1.5 )
	local weaponPickup = textCreateTextItem ( "", 0.5, 0.9, "medium", 255, 255, 255, 255, 1.5 )
	textDisplayAddText ( dynamicTextDisplay, youWereKilledText )
	textDisplayAddText ( dynamicTextDisplay, youKilledText )
	textDisplayAddText ( dynamicTextDisplay, weaponPickup )
	setElementData ( source, "textItem_youWereKilledText", youWereKilledText )
	setElementData ( source, "textItem_youKilledText", youKilledText )
	setElementData ( source, "textItem_weaponPickup", weaponPickup )
	
	--make sure he's an observer of the killing spree text display
	textDisplayAddObserver ( dynamicKillingSpree, source )
end
Everything is echo'd from above, there is no more explaining to do for this.

Triggering dynamic text displays So we've created or dynamic text displays, each unique for every player besides the killing spree text. Now all we need to do is initiate them.

The youWereKilledText and youKilledText are triggered when a player dies, therefore we need to use the onPlayerWasted event.

addEventHandler ( "onPlayerWasted", getRootElement(), "utWastedDynamic" )
function utWastedDynamic ( totalammo, killer, killerweapon, bodypart )
	if ( killer ) and ( killer ~= source ) then
We check if there is a killer, and if there is one, make sure he isnt the same person who died.

Next we setup our youKilledText:

		--Display "You killed x" for the killer
		local youKilledText = getElementData ( killer, "textItem_youKilledText" )
		textItemSetText ( youKilledText, "You killed "..getClientName(source) )
First off we retrieve our youKilledText with element data. Since we want to display the text for the killer (since its "You killed x"), we retrieve the killer's "textItem_youKilledText".

Next we use a different function to previous ones - textItemSetText. The name says it all - it allows setting, or changing the text of a text item. So here we change the blank youKilledText into "You killed "...getClientName(source), where the getClientName retrieves the nickname of the dead player.

		local killerTimer = setTimer ( "textItemSetText", 5000, 1, youKilledText, "" )
As usual, we set a timer to remove the text. This is done by setting the text to something blank using the textItemSetText function.

Then we follow a similar process for youKilledText

		--Display "You were killed by x!" for the person who died
		local youWereKilledText = getElementData ( source, "textItem_youWereKilledText" )
		textItemSetText ( youWereKilledText, "You were killed by "..getClientName(killer).."!" )
		local deadPersonTimer = setTimer ( "textItemSetText", 5000, 1, youWereKilledText, "" )
First off we retrieve the youWereKilledText by using element data. Then, as before, we set the text of this to "You were killed by "..getClientName(killer).."!", this time referencing to the killer as the person who died was killed by the killer.

The last thing that is initiated when a player dies, is the killing spree text.

		local currentSpree = getElementData ( killer, "killingSpree" )
		setElementData ( killer, "killingSpree", currentSpree + 1 )
Similar to the speedkills, we get the current spree of the player, and increase it by one. Unlike speedkills, we do not set it back to 0 after three seconds - sprees remain until a player has died. If you look back at the Preparation section you'll see this being set to 0 when the player spawns.

		showKillingSpree ( killer )
	end
Lastly, we initate a custom showKillingSpree function to handle the showing of the text.

function showKillingSpree ( killer )
	local killingSpree = getElementData ( killer, "killingSpree" )
	local killername = getClientName(killer)
	if killingSpreeTimer == true then
		killTimer ( killingSpreeTimer )
	end
This is the custom showKillingSpree function. The purpose of this is to go about displaying the text for killing sprees.

We start off by retrieving the player's killing spree and defining it. We also define the killer's name. Then we check the global killingSpreeTimer we created earlier. If there is a timer stored there, we can kill it.

	if math.mod ( 25, killingSpree ) == 0 then
The killing spree messages are only initated every 5 kills. Here, we use Lua's math library math.mod function to check if the killing spree is a factor of 25. We specify the ( number, divisor ), and the remainder of the division is returned - so if the remiander is 0, the killing spree must be a factor of 25.

		if killingSpree == 5 then
			textItemSetText ( killingSpreeText, killername.." is on a killing spree!" )
If the killing spree is 5, then we change the killing spree text to 'killername.."is on a killing spree!", where killername is the variable we defined earlier.

We then repeat the process for other factors of 25, with varied messages:

		elseif killingSpree == 10 then
			textItemSetText ( killingSpreeText, killername.." is on a rampage!" )
		elseif killingSpree == 15 then
			textItemSetText ( killingSpreeText, killername.." is dominating!" )
		elseif killingSpree == 20 then
			textItemSetText ( killingSpreeText, killername.." is unstoppable!" )
		elseif killingSpree == 25 then
			textItemSetText ( killingSpreeText, killername.." is Godlike!" )
		end

We then set a timer to blank out the text after 3 seconds, and define it as killingSpreeTimer again, so it can be killed:

		killingSpreeTimer = setTimer ( "textItemSetText", 5000, 1, killingSpreeText, "" )
	end
end

So, we've done our youKilledText, youWereKilledText, and killing spree text. What's left? Our pickup text.

This text shows "You picked up x". It needs to be triggered when a player uses a pickup. Therefore, we use MTA's onPickupUse event.

addEventHandler ( "onPickupUse", getRootElement(), "utPickupUseShowInfo" )
function utPickupUseShowInfo ( player )
As the name suggests, this event is initiated when a player picks up a pickup. The source of this event is the pickup itself.

We start off, as usual, by killing off any timers to destroy the text:

	local oldTimer = getElementData ( player, "textItem_weaponPickupTimer" )
	if oldTimer ~= nil or oldTimer ~= false then
		killTimer ( oldTimer )
	end

We then retrieve the weaponPickup text with element data:

local textPickup = getElementData ( player, "textItem_weaponPickup" )

Now i'll breifly introduce you to MTA's/GTA's pickup system. There are 3 main types of pickups: Armour, health, and weapon pickups.

Health and armour pickups are very alike - they both give a certain amount of health of armour and therefore share a function - getPickupAmount( pickup ). This retrieves the hit points that the pickup represents, and works for both armour and health pickups

Weapon pickups use slightly different functions. There is a getPickupAmmo( pickup ) function, which retrives the amount of ammo a pickup has. There is also a getPickupWeapon( pickup ) - this retrives the weapon ID of the pickup.

So lets move on,

	if getPickupType ( source ) == 0 then
		textItemSetText ( textPickup, "You picked up "..getPickupAmount(source).." health." )
As i stated there are 3 pickup types. Using the getPickupType function we can retrieve the type. the id 0 refers to health, 1 refers to armour, and 2 refers to weapon. So here we check if the pickup is a health, then display "You picked up x health" by setting text.

We do the same thing for armour, only change the text a little:

	elseif getPickupType ( source ) == 1 then
		textItemSetText ( textPickup, "You picked up "..getPickupAmount(source).." armour." )

Weapon pickups are slightly more complex.

First we check that the type of pickup is a weapon:

	elseif getPickupType ( source ) == 2 then

Next i'll introduce some of the weapon functions. The getSlotFromWeapon retrives the slot of a weapon. In GTA, each weapon has a slot - e.g. grenades, smgs, pistols all have different slots. You can see all the weapon slots at http://vces.net/info/WeaponIDs.html (Thanks to Brophy and Ratt for compiling these lists).

So why do we need to know weapon slots? If we want to display messages, they need to have correct grammar! This means we can't have "You picked up 200 ammo for M4" text displaying for grenades..as we'd get "You picked up 5 ammo for Grenades" - but in reality you are picking up 5 grenades

Moving on:

		local slot = getSlotFromWeapon ( getPickupWeapon ( source ) )
		if slot == 0 or slot == 1 or slot == 10 or slot == 12 then
			textItemSetText ( textPickup, "You picked up a "..getWeaponNameFromID(getPickupWeapon(source))..".")
Here we retrieve the slot number from the pickup weapon. If the slot is 0, 1, 10 or 12, then it is a melee style weapon. So we display "You picked up a dildo". We use the getWeaponNameFromID to retrieve the name of the weapon with the ID supplied.

		elseif slot == 8 then
			textItemSetText ( textPickup, "You picked up "..getPickupAmmo(source).." "..getWeaponNameFromID(getPickupWeapon(source)).." explosives.")
		else
			textItemSetText ( textPickup, "You picked up "..getPickupAmmo(source).." ammo for the "..getWeaponNameFromID(getPickupWeapon(source))..".")
		end

Next we repeat the process. All weapons of slot 8 are explosives, so we print "You picked up 5 grenade explosives instead.

Else, all other slots are weapons that have standard ammo/weapon style. So we can have, "You picked up 500 ammo for the M4".

Finally,

	local textTimer = setTimer ( "textItemSetText", 3000, 1, textPickup, "" )
	setElementData ( player, "textItem_weaponPickupTimer", textTimer )
end
We set a timer to set the text to something blank after 3 seconds, and store the timer.

We're done with our UT script. Hurrah.

Though, we're not finished yet. If you look at the video you'll notice new death messages to the right of the screen - also done with text displays. Hows about I explain that too, as compensation for this late tutorial?

Creating our text display and adding observers. First, we create our text display when the resource starts:

addEventHandler ( "onResourceStart", getRootElement(), "createTextOnLoad" )
function createTextOnLoad ( name )
	local outputMessageTextDisplay = textCreateDisplay ()

Next we define some variables for the main settings of the display:

	local distance = 0.027
	local start = 0.25
	local size = 1.2
These variables are the main configuration of the script, and allow you to vary the position of the messages, the size of the text, and the distance in between each text item. These are the default values and will give the appearance as in the scripting video.

We then create our text displays for our death messages:

	outputMessageTextItem1 = textCreateTextItem ( "", 0.8, start + distance, 1, 255, 255, 255, 255, size )
	outputMessageTextItem2 = textCreateTextItem ( "", 0.8, start + (distance * 2 ), 1, 255, 255, 255, 255, size )
	outputMessageTextItem3 = textCreateTextItem ( "", 0.8, start + (distance * 3 ), 1, 255, 255, 255, 255, size )
	outputMessageTextItem4 = textCreateTextItem ( "", 0.8, start + (distance * 4 ), 1, 255, 255, 255, 255, size )
	outputMessageTextItem5 = textCreateTextItem ( "", 0.8, start + (distance * 5 ), 1, 255, 255, 255, 255, size )
	outputMessageTextItem6 = textCreateTextItem ( "", 0.8, start + (distance * 6 ), 1, 255, 255, 255, 255, size )
All the text items are global variables for easy retrival later on. We create 6 text items, all blank. We position them at 0.8 across the screen, and using simple math we position each text item underneath each other in the same intervals according to the distance we set earlier. We create some dummy values for the colour and use the 'size' variable for the size.

	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem1 )
	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem2 )
	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem3 )
	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem4 )
	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem5 )
	textDisplayAddText ( outputMessageTextDisplay, outputMessageTextItem6 )
As you should know, all we do here is add all this text to our text display.

Finally, we make everyone in the server viewers of this text display:

	local players = getElementsByType ( "player" )
	for k,v in players do
		textDisplayAddObserver ( outputMessageTextDisplay, v )
	end
end

We must repeat this for players that join:

addEventHandler ( "onPlayerJoin", getRootElement(), "showTextPlayerJoin" )
function showTextPlayerJoin ()
	textDisplayAddObserver ( outputMessageTextDisplay, source )
end

Setting up our output function

Next, I introduce the custom outputMessage function. This is probably the main part of the script, and handles all incoming text and updates the text display.

It has 4 parameters:outputMessage ( text, r, g, b, a ) Users can specify the text, the colour, and the alpha.

The whole thing is actually very simple. It first retrives all the current text's colours and text, and sets them to the one above, and sets the newest one to the bottom one.

So first it gets all the current text's colour, and text:

	local text2 = textItemGetText ( outputMessageTextItem2 )
	local r2,g2,b2,a2 = textItemGetColor ( outputMessageTextItem2 )
	local text3 = textItemGetText ( outputMessageTextItem3 )
	local r3,g3,b3,a3 = textItemGetColor ( outputMessageTextItem3 )
	local text4 = textItemGetText ( outputMessageTextItem4 )
	local r4,g4,b4,a4 = textItemGetColor ( outputMessageTextItem4 )
	local text5 = textItemGetText ( outputMessageTextItem5 )
	local r5,g5,b5,a5 = textItemGetColor ( outputMessageTextItem5 )
	local text6 = textItemGetText ( outputMessageTextItem6 )
	local r6,g6,b6,a6 = textItemGetColor ( outputMessageTextItem6 )
We get each text item's current text using textItemGetText, and define it textx according to the text item number. We then use textItemGetColor to retrieve 4 values for the colour, and define them as rx, gx, bx, ax.

Next, we move all the text up one:

	textItemSetText ( outputMessageTextItem5, text6 )
	textItemSetColor ( outputMessageTextItem5, r6, g6, b6, a6 )
TextItem5's text becomes TextItem6's. TextItem5's colour becomes TextItem6's.

We do the same for the rest of them:

	textItemSetText ( outputMessageTextItem4, text5 )
	textItemSetColor ( outputMessageTextItem4, r5, g5, b5, a5 )
	textItemSetText ( outputMessageTextItem3, text4 )
	textItemSetColor ( outputMessageTextItem3, r4, g4, b4, a4 )
	textItemSetText ( outputMessageTextItem2, text3 )
	textItemSetColor ( outputMessageTextItem2, r3, g3, b3, a3 )
	textItemSetText ( outputMessageTextItem1, text2 )
	textItemSetColor ( outputMessageTextItem1, r2, g2, b2, a2 )

So we've moved all our text up one. We need to add the new text passed into the function as the bottom text:

	textItemSetText ( outputMessageTextItem6, text )
	textItemSetColor ( outputMessageTextItem6, r, g, b, a )
TextItem6's text becomes the text passed into the function. TextItem6's colours become the colours passed into the function. Its not that complicated.

Outputting our messages Lastly, we have to actually have the event to trigger the outputMessage function.

addEventHandler ( "onPlayerWasted", getRootElement(), "KillMessages_onPlayerWasted" )
function KillMessages_onPlayerWasted ( totalammo, killer, killerweapon, bodypart )
	-- Grab the nick of the killed player
	local killed_nick = getClientName ( source )
	local r,g,b = getPlayerNametagColor	( source )
We add an event handler for onPlayerWasted. We then get the nickname of the player who died. We then use the getPlayerNametagColor to retrieve the current player's nametag colour (that's right, nametag colours are changable), and define it as r,g,b.

	-- Got a killer? Print the normal "* X died" if not
	if ( killer ) then
		-- Suicide?
		if (source == killer) then
			killed_nick = "himself"
		end
Here, we check if there is a killer, then check if the killer is the person who died. If he is, then we define killed_nick as "himself".

		-- Populate a "' X killed Y" killmessage
		local killmessage = getClientName(killer).." killed "..killed_nick
		-- Grab the name of the killer weapon. Got any? Print the message with or without the weapon name
We then define killmessage as the actual message we'll be displaying eventually. So it displays "getClientName(killer).." killed "..killed_nick . E.g. "Talidan killed Brophy". If we however found that that source == killer, then it'd display "Talidan killed himself" since the killed_nick is changed.

		local killerweapon_name = getWeaponNameFromID ( killerweapon )
		if ( killerweapon_name ) then
			outputMessage( killmessage.." ("..killerweapon_name..")",  r, g, b, 255 )
We then check if there was a weapon used at the scene of the crime. This is done with the if statement - we check if a name could be retrieved from the weapon. If it could be retrived, call the outputMessage function, with the killmessage, then the weapon in brackets. e.g. "Talidan killed Brophy (Dildo)". We then pass the colour as r, g and b i.e. the nametag colour we retrieved earlier, and an alpha of 255.

However, if there was not a weapon involved:

		else
			outputMessage( killmessage, r, g, b, 255 )
		end
Just output the message without any brackets, with the killmessage and colours as before.

	else
		outputMessage( killed_nick.." died", r, g, b, 255 )
	end
end
Lastly, if there wasnt a killer at all, then we just display "killed_nick died" e.g. "Talidan died".

And that's all there is to it!

Before we finish, i'd just like to say that the kill messages script is designed to output anything into its display using the outputMessage ( text, r, g, b, a ) function. In this example i used it for death messages. But you, as a scripter, could display anything you wanted to announce there.

Thanks for reading, and sorry for the delay. Have a very merry christmas.