added amb1424 - hudtext functions for mods that support them

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401921
This commit is contained in:
David Anderson 2008-03-02 23:54:38 +00:00
parent e0b4b8b9f6
commit e7f8d1b2f5
7 changed files with 658 additions and 4 deletions

View File

@ -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));

View File

@ -1424,6 +1424,10 @@
RelativePath="..\smn_handles.cpp"
>
</File>
<File
RelativePath="..\smn_hudtext.cpp"
>
</File>
<File
RelativePath="..\smn_keyvalues.cpp"
>

View File

@ -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
*/

475
core/smn_hudtext.cpp Normal file
View File

@ -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},
};

View File

@ -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);

View File

@ -64,6 +64,24 @@
}
}
/**
* Which games support HudMsg?
*/
"#default"
{
"#supported"
{
"game" "hl2mp"
"game" "sourceforts"
"game" "tf"
}
"Keys"
{
"HudTextMsg" "HudMsg"
}
}
/**
* Which games use an extra byte in the HintText
* message? Even though it's in the SDK, apparently

View File

@ -353,14 +353,155 @@ 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.
*