467 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * vim: set ts=4 :
 | |
|  * =============================================================================
 | |
|  * SourceMod
 | |
|  * Copyright (C) 2004-2008 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[256 + 1];
 | |
| };
 | |
| 
 | |
| 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;
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 		HandleError err;
 | |
| 		hud_syncobj_t *obj;
 | |
| 		HandleSecurity sec;
 | |
| 
 | |
| 		obj = new hud_syncobj_t;
 | |
| 
 | |
| 		memset(obj->player_channels, 0, sizeof(obj->player_channels));
 | |
| 
 | |
| 		sec = HandleSecurity(pIdent, g_pCoreIdent);
 | |
| 		
 | |
| 		if ((hndl = g_HandleSys.CreateHandleEx(m_hHudSyncObj, obj, &sec, NULL, &err))
 | |
| 			== BAD_HANDLE)
 | |
| 		{
 | |
| 			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->GetLastNativeError() != 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->GetLastNativeError() != 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},
 | |
| };
 |