/** * 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 . * * 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 . * * 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(params[4]); g_hud_params.g1 = static_cast(params[5]); g_hud_params.b1 = static_cast(params[6]); g_hud_params.a1 = static_cast(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(color1[0]); g_hud_params.g1 = static_cast(color1[1]); g_hud_params.b1 = static_cast(color1[2]); g_hud_params.a1 = static_cast(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(color2[0]); g_hud_params.g2 = static_cast(color2[1]); g_hud_params.b2 = static_cast(color2[2]); g_hud_params.a2 = static_cast(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}, };