From e7f8d1b2f53a70a4fc718244eb45e959c541f0bf Mon Sep 17 00:00:00 2001
From: David Anderson <dvander@alliedmods.net>
Date: Sun, 2 Mar 2008 23:54:38 +0000
Subject: [PATCH] added amb1424 - hudtext functions for mods that support them

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401921
---
 core/PlayerManager.cpp         |   1 +
 core/msvc8/sourcemod_mm.vcproj |   4 +
 core/sm_globals.h              |   9 +-
 core/smn_hudtext.cpp           | 475 +++++++++++++++++++++++++++++++++
 core/sourcemod.cpp             |   8 +
 gamedata/core.games.txt        |  18 ++
 plugins/include/halflife.inc   | 147 +++++++++-
 7 files changed, 658 insertions(+), 4 deletions(-)
 create mode 100644 core/smn_hudtext.cpp

diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp
index ac1fc427..b6f37ca5 100644
--- a/core/PlayerManager.cpp
+++ b/core/PlayerManager.cpp
@@ -90,6 +90,7 @@ PlayerManager::PlayerManager()
 {
 	m_AuthQueue = NULL;
 	m_FirstPass = false;
+	m_maxClients = 0;
 
 	m_UserIdLookUp = new int[USHRT_MAX+1];
 	memset(m_UserIdLookUp, 0, sizeof(int) * (USHRT_MAX+1));
diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj
index c3efbe2c..3c5985a0 100644
--- a/core/msvc8/sourcemod_mm.vcproj
+++ b/core/msvc8/sourcemod_mm.vcproj
@@ -1424,6 +1424,10 @@
 				RelativePath="..\smn_handles.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\smn_hudtext.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\smn_keyvalues.cpp"
 				>
diff --git a/core/sm_globals.h b/core/sm_globals.h
index b761fabf..42fa6b15 100644
--- a/core/sm_globals.h
+++ b/core/sm_globals.h
@@ -85,12 +85,19 @@ public:
 	}
 
 	/**
-	 * @brief Called after all global classes have initialized
+	 * @brief Called after all global classes have been started up
 	 */
 	virtual void OnSourceModAllInitialized()
 	{
 	}
 
+	/**
+	 * @brief Called after all global classes have initialized
+	 */
+	virtual void OnSourceModAllInitialized_Post()
+	{
+	}
+
 	/**
 	 * @brief Called when SourceMod is shutting down
 	 */
diff --git a/core/smn_hudtext.cpp b/core/smn_hudtext.cpp
new file mode 100644
index 00000000..50d1923a
--- /dev/null
+++ b/core/smn_hudtext.cpp
@@ -0,0 +1,475 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation.  You must obey the GNU General Public License in
+ * all respects for all other code used.  Additionally, AlliedModders LLC grants
+ * this exception to all derivative works.  AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or <http://www.sourcemod.net/license.php>.
+ *
+ * Version: $Id$
+ */
+
+#include "sm_globals.h"
+#include "GameConfigs.h"
+#include "UserMessages.h"
+#include "TimerSys.h"
+#include "PlayerManager.h"
+#include "HandleSys.h"
+
+#define MAX_HUD_CHANNELS		6
+
+int g_HudMsgNum = -1;
+
+struct hud_syncobj_t
+{
+	int *player_channels;
+};
+
+struct player_chaninfo_t
+{
+	double chan_times[MAX_HUD_CHANNELS];
+	hud_syncobj_t *chan_syncobjs[MAX_HUD_CHANNELS];
+};
+
+struct hud_text_parms
+{
+	float       x;
+	float       y;
+	int         effect;
+	byte        r1, g1, b1, a1;
+	byte        r2, g2, b2, a2;
+	float       fadeinTime;
+	float       fadeoutTime;
+	float       holdTime;
+	float       fxTime;
+	int         channel;
+};
+
+class HudMsgHelpers : 
+	public SMGlobalClass,
+	public IHandleTypeDispatch,
+	public IClientListener
+{
+public:
+	bool IsSupported()
+	{
+		return (g_HudMsgNum != -1);
+	}
+
+	virtual void OnSourceModAllInitialized_Post()
+	{
+		const char *key;
+
+		key = g_pGameConf->GetKeyValue("HudTextMsg");
+		if (key != NULL)
+		{
+			g_HudMsgNum = g_UserMsgs.GetMessageIndex(key);
+		}
+
+		if (!IsSupported())
+		{
+			m_hHudSyncObj = 0;
+			m_PlayerHuds = NULL;
+			return;
+		}
+
+		m_PlayerHuds = new player_chaninfo_t[256+1];
+		m_hHudSyncObj = g_HandleSys.CreateType("HudSyncObj", this, 0, NULL, NULL, g_pCoreIdent, NULL);
+
+		g_Players.AddClientListener(this);
+	}
+
+	virtual void OnSourceModShutdown()
+	{
+		if (!IsSupported())
+		{
+			return;
+		}
+
+		delete [] m_PlayerHuds;
+		g_HandleSys.RemoveType(m_hHudSyncObj, g_pCoreIdent);
+
+		g_Players.RemoveClientListener(this);
+	}
+
+	virtual void OnHandleDestroy(HandleType_t type, void *object)
+	{
+		hud_syncobj_t *obj = (hud_syncobj_t *)object;
+
+		delete [] obj->player_channels;
+		delete obj;
+	}
+
+	virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
+	{
+		*pSize = sizeof(unsigned int) * g_Players.GetMaxClients();
+		return true;
+	}
+
+	virtual void OnClientConnected(int client)
+	{
+		player_chaninfo_t *player;
+
+		player = &m_PlayerHuds[client];
+		memset(player->chan_syncobjs, 0, sizeof(player->chan_syncobjs));
+		memset(player->chan_times, 0, sizeof(player->chan_times));
+	}
+
+	Handle_t CreateHudSyncObj(IdentityToken_t *pIdent)
+	{
+		Handle_t hndl;
+		int max_clients;
+		HandleError err;
+		hud_syncobj_t *obj;
+		HandleSecurity sec;
+
+		if ((max_clients = g_Players.MaxClients()) == 0)
+		{
+			max_clients = 256+1;
+		}
+
+		obj = new hud_syncobj_t;
+		obj->player_channels = new int[max_clients];
+
+		memset(obj->player_channels, 0, sizeof(int) * max_clients);
+
+		sec = HandleSecurity(pIdent, g_pCoreIdent);
+		
+		if ((hndl = g_HandleSys.CreateHandleEx(m_hHudSyncObj, obj, &sec, NULL, &err))
+			== BAD_HANDLE)
+		{
+			delete [] obj->player_channels;
+			delete obj;
+		}
+
+		return hndl;
+	}
+
+	HandleError ReadHudSyncObj(Handle_t hndl, 
+		IdentityToken_t *pOwner, 
+		hud_syncobj_t **pObj)
+	{
+		HandleSecurity sec(pOwner, g_pCoreIdent);
+		return g_HandleSys.ReadHandle(hndl, m_hHudSyncObj, &sec, (void **)pObj);
+	}
+
+	unsigned int AutoSelectChannel(unsigned int client)
+	{
+		int last_channel;
+		player_chaninfo_t *player;
+
+		player = &m_PlayerHuds[client];
+
+		last_channel = 0;
+		for (unsigned int i = 1; i < MAX_HUD_CHANNELS; i++)
+		{
+			if (player->chan_times[i] < player->chan_times[last_channel])
+			{
+				last_channel = i;
+			}
+		}
+
+		ManualSelectChannel(client, last_channel);
+
+		return last_channel;
+	}
+
+	int TryReuseLastChannel(unsigned int client, hud_syncobj_t *obj)
+	{
+		int last_channel;
+		player_chaninfo_t *player;
+
+		player = &m_PlayerHuds[client];
+
+		/* First, see if we can re-use the previous channel. */
+		last_channel = obj->player_channels[client];
+
+		if (player->chan_syncobjs[last_channel] == obj)
+		{
+			player->chan_times[last_channel] = *g_pUniversalTime;
+			return last_channel;
+		}
+
+		return -1;
+	}
+
+	int AutoSelectChannel(unsigned int client, hud_syncobj_t *obj)
+	{
+		int last_channel;
+		player_chaninfo_t *player;
+
+		player = &m_PlayerHuds[client];
+		
+		/* First, see if we can re-use the previous channel. */
+		last_channel = obj->player_channels[client];
+
+		if (player->chan_syncobjs[last_channel] == obj)
+		{
+			player->chan_times[last_channel] = *g_pUniversalTime;
+			return last_channel;
+		}
+
+		last_channel = 0;
+		for (unsigned int i = 1; i < MAX_HUD_CHANNELS; i++)
+		{
+			if (player->chan_times[i] < player->chan_times[last_channel])
+			{
+				last_channel = i;
+			}
+		}
+
+		obj->player_channels[client] = last_channel;
+		player->chan_syncobjs[last_channel] = obj;
+		player->chan_times[last_channel] = *g_pUniversalTime;
+
+		return last_channel;
+	}
+
+	int ManualSelectChannel(unsigned int client, int channel)
+	{
+		player_chaninfo_t *player;
+
+		player = &m_PlayerHuds[client];
+		player->chan_times[channel] = *g_pUniversalTime;
+		player->chan_syncobjs[channel] = NULL;
+
+		return channel;
+	}
+private:
+	HandleType_t m_hHudSyncObj;
+	player_chaninfo_t *m_PlayerHuds;
+} s_HudMsgHelpers;
+
+hud_text_parms g_hud_params;
+
+static cell_t CreateHudSynchronizer(IPluginContext *pContext, const cell_t *params)
+{
+	return s_HudMsgHelpers.CreateHudSyncObj(pContext->GetIdentity());
+}
+
+static cell_t SetHudTextParams(IPluginContext *pContext, const cell_t *params)
+{
+	g_hud_params.x = sp_ctof(params[1]);
+	g_hud_params.y = sp_ctof(params[2]);
+	g_hud_params.holdTime = sp_ctof(params[3]);
+	g_hud_params.r1 = static_cast<byte>(params[4]);
+	g_hud_params.g1 = static_cast<byte>(params[5]);
+	g_hud_params.b1 = static_cast<byte>(params[6]);
+	g_hud_params.a1 = static_cast<byte>(params[7]);
+	g_hud_params.effect = params[8];
+	g_hud_params.fxTime = sp_ctof(params[9]);
+	g_hud_params.fadeinTime = sp_ctof(params[10]);
+	g_hud_params.fadeoutTime = sp_ctof(params[11]);
+	g_hud_params.r2 = 255;
+	g_hud_params.g2 = 255;
+	g_hud_params.b2 = 250;
+	g_hud_params.a2 = 0;
+
+	return 1;
+}
+
+static cell_t SetHudTextParamsEx(IPluginContext *pContext, const cell_t *params)
+{
+	cell_t *color1, *color2;
+
+	pContext->LocalToPhysAddr(params[4], &color1);
+	pContext->LocalToPhysAddr(params[5], &color2);
+
+	g_hud_params.x = sp_ctof(params[1]);
+	g_hud_params.y = sp_ctof(params[2]);
+	g_hud_params.holdTime = sp_ctof(params[3]);
+	g_hud_params.r1 = static_cast<byte>(color1[0]);
+	g_hud_params.g1 = static_cast<byte>(color1[1]);
+	g_hud_params.b1 = static_cast<byte>(color1[2]);
+	g_hud_params.a1 = static_cast<byte>(color1[3]);
+	g_hud_params.effect = params[6];
+	g_hud_params.fxTime = sp_ctof(params[7]);
+	g_hud_params.fadeinTime = sp_ctof(params[8]);
+	g_hud_params.fadeoutTime = sp_ctof(params[9]);
+	g_hud_params.r2 = static_cast<byte>(color2[0]);
+	g_hud_params.g2 = static_cast<byte>(color2[1]);
+	g_hud_params.b2 = static_cast<byte>(color2[2]);
+	g_hud_params.a2 = static_cast<byte>(color2[3]);
+
+	return 1;
+}
+
+void UTIL_SendHudText(int client, const hud_text_parms &textparms, const char *pMessage)
+{
+	bf_write *bf;
+	cell_t players[1];
+
+	players[0] = client;
+
+	bf = g_UserMsgs.StartMessage(g_HudMsgNum, players, 1, 0);
+	bf->WriteByte(textparms.channel & 0xFF );
+	bf->WriteFloat(textparms.x);
+	bf->WriteFloat(textparms.y);
+	bf->WriteByte(textparms.r1);
+	bf->WriteByte(textparms.g1);
+	bf->WriteByte(textparms.b1);
+	bf->WriteByte(textparms.a1);
+	bf->WriteByte(textparms.r2);
+	bf->WriteByte(textparms.g2);
+	bf->WriteByte(textparms.b2);
+	bf->WriteByte(textparms.a2);
+	bf->WriteByte(textparms.effect);
+	bf->WriteFloat(textparms.fadeinTime);
+	bf->WriteFloat(textparms.fadeoutTime);
+	bf->WriteFloat(textparms.holdTime);
+	bf->WriteFloat(textparms.fxTime);
+	bf->WriteString(pMessage);
+	g_UserMsgs.EndMessage();
+}
+
+static cell_t ShowSyncHudText(IPluginContext *pContext, const cell_t *params)
+{
+	int client;
+	Handle_t err;
+	CPlayer *pPlayer;
+	hud_syncobj_t *obj;
+	char message_buffer[255-36];
+
+	if (!s_HudMsgHelpers.IsSupported())
+	{
+		return -1;
+	}
+
+	if ((err = s_HudMsgHelpers.ReadHudSyncObj(params[2], pContext->GetIdentity(), &obj)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[2], err);
+	}
+
+	client = params[1];
+	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
+	{
+		return pContext->ThrowNativeError("Invalid client index %d", client);
+	}
+	else if (!pPlayer->IsInGame())
+	{
+		return pContext->ThrowNativeError("Client %d is not in-game", client);
+	}
+
+	g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
+	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
+	{
+		return 0;
+	}
+
+	g_hud_params.channel = s_HudMsgHelpers.AutoSelectChannel(client, obj);
+	UTIL_SendHudText(client, g_hud_params, message_buffer);
+
+	return 1;
+}
+
+static cell_t ClearSyncHud(IPluginContext *pContext, const cell_t *params)
+{
+	int client;
+	int channel;
+	Handle_t err;
+	CPlayer *pPlayer;
+	hud_syncobj_t *obj;
+
+	if (!s_HudMsgHelpers.IsSupported())
+	{
+		return -1;
+	}
+
+	if ((err = s_HudMsgHelpers.ReadHudSyncObj(params[2], pContext->GetIdentity(), &obj)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[2], err);
+	}
+
+	client = params[1];
+	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
+	{
+		return pContext->ThrowNativeError("Invalid client index %d", client);
+	}
+	else if (!pPlayer->IsInGame())
+	{
+		return pContext->ThrowNativeError("Client %d is not in-game", client);
+	}
+
+	if ((channel = s_HudMsgHelpers.TryReuseLastChannel(client, obj)) == -1)
+	{
+		return -1;
+	}
+
+	g_hud_params.channel = channel;
+	UTIL_SendHudText(client, g_hud_params, "");
+
+	return g_hud_params.channel;
+}
+
+static cell_t ShowHudText(IPluginContext *pContext, const cell_t *params)
+{
+	int client;
+	CPlayer *pPlayer;
+	char message_buffer[255-36];
+
+	if (!s_HudMsgHelpers.IsSupported())
+	{
+		return -1;
+	}
+
+	client = params[1];
+	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
+	{
+		return pContext->ThrowNativeError("Invalid client index %d", client);
+	}
+	else if (!pPlayer->IsInGame())
+	{
+		return pContext->ThrowNativeError("Client %d is not in-game", client);
+	}
+
+	g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
+	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
+	{
+		return 0;
+	}
+
+	if (params[2] == -1)
+	{
+		g_hud_params.channel = s_HudMsgHelpers.AutoSelectChannel(client);
+	}
+	else
+	{
+		g_hud_params.channel = params[2] % MAX_HUD_CHANNELS;
+		s_HudMsgHelpers.ManualSelectChannel(client, g_hud_params.channel);
+	}
+
+	UTIL_SendHudText(client, g_hud_params, message_buffer);
+
+	return g_hud_params.channel;
+}
+
+REGISTER_NATIVES(hudNatives)
+{
+	{"ClearSyncHud",				ClearSyncHud},
+	{"CreateHudSynchronizer",		CreateHudSynchronizer},
+	{"SetHudTextParams",			SetHudTextParams},
+	{"SetHudTextParamsEx",			SetHudTextParamsEx},
+	{"ShowHudText",					ShowHudText},
+	{"ShowSyncHudText",				ShowSyncHudText},
+	{NULL,							NULL},
+};
diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp
index 5a44e775..695e9933 100644
--- a/core/sourcemod.cpp
+++ b/core/sourcemod.cpp
@@ -302,6 +302,14 @@ void SourceModBase::StartSourceMod(bool late)
 		pBase = pBase->m_pGlobalClassNext;
 	}
 
+	/* Notify! */
+	pBase = SMGlobalClass::head;
+	while (pBase)
+	{
+		pBase->OnSourceModAllInitialized_Post();
+		pBase = pBase->m_pGlobalClassNext;
+	}
+
 	/* Add us now... */
 	g_ShareSys.AddInterface(NULL, this);
 
diff --git a/gamedata/core.games.txt b/gamedata/core.games.txt
index f598813a..2d91a614 100644
--- a/gamedata/core.games.txt
+++ b/gamedata/core.games.txt
@@ -63,6 +63,24 @@
 			"HudRadioMenuMsg"		"ShowMenu"
 		}
 	}
+	
+	/**
+	 * Which games support HudMsg?
+	 */
+	"#default"
+	{
+		"#supported"
+		{
+			"game"		"hl2mp"
+			"game"		"sourceforts"
+			"game"		"tf"
+		}
+		
+		"Keys"
+		{
+			"HudTextMsg"			"HudMsg"
+		}
+	}
 
 	/**
 	 * Which games use an extra byte in the HintText 
diff --git a/plugins/include/halflife.inc b/plugins/include/halflife.inc
index c9f1fbba..35fd7f74 100644
--- a/plugins/include/halflife.inc
+++ b/plugins/include/halflife.inc
@@ -353,21 +353,162 @@ stock PrintHintTextToAll(const String:format[], any:...)
  * Shows a VGUI panel to a specific client.
  *
  * @param client		Client index.
- * @param name			Panel type name (Check viewport_panel_names.h to see a list of some panel names).
- * @param Kv			KeyValues handle with all the data for the panel setup (Depends on the panel type and may be unused).
+ * @param name			Panel type name (Check viewport_panel_names.h to see a list of 
+ *						some panel names).
+ * @param Kv			KeyValues handle with all the data for the panel setup (Depends 
+ *						on the panel type and may be unused).
  * @param show			True to show the panel, or false to remove it from the client screen.
  * @noreturn
  * @error				If the client is not connected an error will be thrown.
  */
 native ShowVGUIPanel(client, const String:name[], Handle:Kv=INVALID_HANDLE, bool:show=true);
 
+/**
+ * Creates a HUD synchronization object.  This object is used to automatically assign and 
+ * re-use channels for a set of messages.  
+ *
+ * The HUD has a hardcoded number of channels (usually 6) for displaying 
+ * text.  You can use any channel for any area of the screen.  Text on 
+ * different channels can overlap, but text on the same channel will 
+ * erase the old text first.  This overlapping and overwriting gets problematic.
+ *
+ * A HUD synchronization object automatically selects channels for you based on 
+ * the following heuristics:
+ *  - If channel X was last used by the object, and hasn't been modified again, 
+ *    channel X gets re-used.
+ *  - Otherwise, a new channel is chosen based on the least-recently-used channel.
+ *
+ * This ensures that if you display text on a sync object, that the previous text 
+ * displayed on it will always be cleared first.  This is because your new text 
+ * will either overwrite the old text on the same channel, or because another 
+ * channel has already erased your text.
+ *
+ * Note that messages can still overlap if they are on different synchronization 
+ * objects, or they are displayed to manual channels.
+ *
+ * These are particularly useful for displaying repeating or refreshing HUD text, in 
+ * addition to displaying multiple message sets in one area of the screen (for example, 
+ * center-say messages that may pop up randomly that you don't want to overlap each 
+ * other).
+ *
+ * @return				New HUD synchronization object.
+ *						The Handle can be closed with CloseHandle().
+ *						If HUD text is not supported on this mod, then 
+ *						INVALID_HANDLE is returned.
+ */
+native Handle:CreateHudSynchronizer();
+
+/**
+ * Sets the HUD parameters for drawing text.  These parameters are stored 
+ * globally, although nothing other than this function and SetHudTextParamsEx 
+ * modify them.
+ *
+ * You must call this function before drawing text.  If you are drawing 
+ * text to multiple clients, you can set the parameters once, since 
+ * they won't be modified.  However, as soon as you pass control back 
+ * to other plugins, you must reset the parameters next time you draw.
+ *
+ * @param x				x coordinate, from 0 to 1.  -1.0 is the center.
+ * @param y				y coordinate, from 0 to 1.  -1.0 is the center.
+ * @param holdTime		Number of seconds to hold the text.
+ * @param r				Red color value.
+ * @param g				Green color value.
+ * @param b				Blue color value.
+ * @param a				Alpha transparency value.
+ * @param effect 		0/1 causes the text to fade in and fade out.
+ *						2 causes the text to flash[?].
+ * @param fxTime		Duration of chosen effect (may not apply to all effects).
+ * @param fadeIn		Number of seconds to spend fading in.
+ * @param fadeOut		Number of seconds to spend fading out.
+ * @noreturn
+ */
+native SetHudTextParams(Float:x, Float:y, Float:holdTime, r, g, b, a, effect = 0,
+						Float:fxTime=6.0, Float:fadeIn=0.1, Float:fadeOut=0.2);
+						
+/**
+ * Sets the HUD parameters for drawing text.  These parameters are stored 
+ * globally, although nothing other than this function and SetHudTextParams
+ * modify them.
+ *
+ * This is the same as SetHudTextParams(), except it lets you set the alternate 
+ * color for when effects require it.  
+ *
+ * @param x				x coordinate, from 0 to 1.  -1.0 is the center.
+ * @param y				y coordinate, from 0 to 1.  -1.0 is the center.
+ * @param holdTime		Number of seconds to hold the text.
+ * @param color1		First color set, array values being [red, green, blue, alpha]
+ * @param color2		Second color set, array values being [red, green, blue, alpha]
+ * @param effect 		0/1 causes the text to fade in and fade out.
+ *						2 causes the text to flash[?].
+ * @param fxTime		Duration of chosen effect (may not apply to all effects).
+ * @param fadeIn		Number of seconds to spend fading in.
+ * @param fadeOut		Number of seconds to spend fading out.
+ * @noreturn
+ */
+native SetHudTextParamsEx(Float:x, Float:y, Float:holdTime, color1[4], 
+						  color2[4]={255,255,255,0}, effect = 0, Float:fxTime=6.0, 
+						  Float:fadeIn=0.1, Float:fadeOut=0.2);
+
+/**
+ * Shows a synchronized HUD message to a client.  
+ *
+ * As of this writing, only TF, HL2MP, and SourceForts support HUD Text.
+ *
+ * @param client		Client index to send the message to.
+ * @param sync			Synchronization object.
+ * @param message		Message text or formatting rules.
+ * @param ...			Message formatting parameters.
+ * @return				-1 on failure, anything else on success.
+ *						This function fails if the mod does not support it.
+ * @error				Client not in-game, or sync object not valid.
+ */
+native ShowSyncHudText(client, Handle:sync, const String:message[], any:...);
+
+/**
+ * Clears the text on a synchronized HUD channel. 
+ *
+ * This is not the same as sending "" because it guarantees that it won't 
+ * overwrite text on another channel.  For example, consider the scenario: 
+ *
+ * 1. Your synchronized message goes to channel 3.
+ * 2. Someone else's non-synchronized message goes to channel 3.
+ *
+ * If you were to simply send "" on your synchronized message, 
+ * then someone else's text could be overwritten.
+ *
+ * @param client		Client index to send the message to.
+ * @param sync			Synchronization object.
+ * @noreturn
+ * @error				Client not in-game, or sync object not valid.
+ */
+native ClearSyncHud(client, Handle:sync);
+
+/**
+ * Shows a HUD message to a client on the given channel.
+ *
+ * As of this writing, only TF, HL2MP, and SourceForts support HUD Text.
+ * 
+ * @param client		Client index to send the message to.
+ * @param channel		A channel number.
+ *						If -1, then a channel will automatically be selected 
+ *						based on the least-recently-used channel.  If the 
+ *						channel is any other number, it will be modulo'd with 
+ *						the channel count to get a final channel number.
+ * @param message		Message text or formatting rules.
+ * @param ...			Message formatting parameters.
+ * @return				-1 on failure (lack of mod support).
+ *						Any other return value is the channel number that was 
+ *						used to render the text.
+ */
+native ShowHudText(client, channel, const String:message[], any:...);
+
 /**
  * Shows a MOTD panel to a specific client.
  *
  * @param client		Client index.
  * @param title			Title of the panel (printed on the top border of the window).
  * @param msg			Contents of the panel, it can be treated as an url, filename or plain text
- *				  depending on the type parameter (WARNING: msg has to be 192 bytes maximum!)
+ *				 		depending on the type parameter (WARNING: msg has to be 192 bytes maximum!)
  * @param type			Determines the way to treat the message body of the panel.
  * @noreturn
  * @error				If the client is not connected an error will be thrown.