dvander@1915: /** dvander@1915: * vim: set ts=4 : dvander@1915: * ============================================================================= dvander@1915: * SourceMod ds@1947: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. dvander@1915: * ============================================================================= dvander@1915: * dvander@1915: * This program is free software; you can redistribute it and/or modify it under dvander@1915: * the terms of the GNU General Public License, version 3.0, as published by the dvander@1915: * Free Software Foundation. dvander@1915: * dvander@1915: * This program is distributed in the hope that it will be useful, but WITHOUT dvander@1915: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@1915: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more dvander@1915: * details. dvander@1915: * dvander@1915: * You should have received a copy of the GNU General Public License along with dvander@1915: * this program. If not, see . dvander@1915: * dvander@1915: * As a special exception, AlliedModders LLC gives you permission to link the dvander@1915: * code of this program (as well as its derivative works) to "Half-Life 2," the dvander@1915: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software dvander@1915: * by the Valve Corporation. You must obey the GNU General Public License in dvander@1915: * all respects for all other code used. Additionally, AlliedModders LLC grants dvander@1915: * this exception to all derivative works. AlliedModders LLC defines further dvander@1915: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), dvander@1915: * or . dvander@1915: * dvander@1915: * Version: $Id$ dvander@1915: */ dvander@1915: dvander@1915: #include "sm_globals.h" dvander@3010: #include dvander@1915: #include "UserMessages.h" dvander@1915: #include "TimerSys.h" dvander@1915: #include "PlayerManager.h" dvander@3010: #include "logic_bridge.h" dvander@3872: #include "sourcemod.h" dvander@1915: skamonkey@3855: #if SOURCE_ENGINE == SE_DOTA skamonkey@3855: #include skamonkey@3855: #elif SOURCE_ENGINE == SE_CSGO skamonkey@3743: #include skamonkey@3743: #endif skamonkey@3743: dvander@1915: #define MAX_HUD_CHANNELS 6 dvander@1915: dvander@1915: int g_HudMsgNum = -1; dvander@1915: dvander@1915: struct hud_syncobj_t dvander@1915: { dvander@2816: int player_channels[256 + 1]; dvander@1915: }; dvander@1915: dvander@1915: struct player_chaninfo_t dvander@1915: { dvander@1915: double chan_times[MAX_HUD_CHANNELS]; dvander@1915: hud_syncobj_t *chan_syncobjs[MAX_HUD_CHANNELS]; dvander@1915: }; dvander@1915: dvander@1915: struct hud_text_parms dvander@1915: { dvander@1915: float x; dvander@1915: float y; dvander@1915: int effect; dvander@1915: byte r1, g1, b1, a1; dvander@1915: byte r2, g2, b2, a2; dvander@1915: float fadeinTime; dvander@1915: float fadeoutTime; dvander@1915: float holdTime; dvander@1915: float fxTime; dvander@1915: int channel; dvander@1915: }; dvander@1915: dvander@1915: class HudMsgHelpers : dvander@1915: public SMGlobalClass, dvander@1915: public IHandleTypeDispatch, dvander@1915: public IClientListener dvander@1915: { dvander@1915: public: dvander@1915: bool IsSupported() dvander@1915: { dvander@1915: return (g_HudMsgNum != -1); dvander@1915: } dvander@1915: dvander@1915: virtual void OnSourceModAllInitialized_Post() dvander@1915: { dvander@1915: const char *key; dvander@1915: dvander@1915: key = g_pGameConf->GetKeyValue("HudTextMsg"); dvander@1915: if (key != NULL) dvander@1915: { dvander@1915: g_HudMsgNum = g_UserMsgs.GetMessageIndex(key); dvander@1915: } dvander@1915: dvander@1915: if (!IsSupported()) dvander@1915: { dvander@1915: m_hHudSyncObj = 0; dvander@1915: m_PlayerHuds = NULL; dvander@1915: return; dvander@1915: } dvander@1915: dvander@1915: m_PlayerHuds = new player_chaninfo_t[256+1]; dvander@3872: m_hHudSyncObj = handlesys->CreateType("HudSyncObj", this, 0, NULL, NULL, g_pCoreIdent, NULL); dvander@1915: dvander@1915: g_Players.AddClientListener(this); dvander@1915: } dvander@1915: dvander@1915: virtual void OnSourceModShutdown() dvander@1915: { dvander@1915: if (!IsSupported()) dvander@1915: { dvander@1915: return; dvander@1915: } dvander@1915: dvander@1915: delete [] m_PlayerHuds; dvander@3872: handlesys->RemoveType(m_hHudSyncObj, g_pCoreIdent); dvander@1915: dvander@1915: g_Players.RemoveClientListener(this); dvander@1915: } dvander@1915: dvander@1915: virtual void OnHandleDestroy(HandleType_t type, void *object) dvander@1915: { dvander@1915: hud_syncobj_t *obj = (hud_syncobj_t *)object; dvander@1915: dvander@1915: delete obj; dvander@1915: } dvander@1915: dvander@1915: virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) dvander@1915: { dvander@1915: *pSize = sizeof(unsigned int) * g_Players.GetMaxClients(); dvander@1915: return true; dvander@1915: } dvander@1915: dvander@1915: virtual void OnClientConnected(int client) dvander@1915: { dvander@1915: player_chaninfo_t *player; dvander@1915: dvander@1915: player = &m_PlayerHuds[client]; dvander@1915: memset(player->chan_syncobjs, 0, sizeof(player->chan_syncobjs)); dvander@1915: memset(player->chan_times, 0, sizeof(player->chan_times)); dvander@1915: } dvander@1915: dvander@1915: Handle_t CreateHudSyncObj(IdentityToken_t *pIdent) dvander@1915: { dvander@1915: Handle_t hndl; dvander@1915: HandleError err; dvander@1915: hud_syncobj_t *obj; dvander@1915: HandleSecurity sec; dvander@1915: dvander@2816: obj = new hud_syncobj_t; dvander@1915: dvander@2816: memset(obj->player_channels, 0, sizeof(obj->player_channels)); dvander@1915: dvander@1915: sec = HandleSecurity(pIdent, g_pCoreIdent); dvander@1915: dvander@3872: if ((hndl = handlesys->CreateHandleEx(m_hHudSyncObj, obj, &sec, NULL, &err)) dvander@1915: == BAD_HANDLE) dvander@1915: { dvander@1915: delete obj; dvander@1915: } dvander@1915: dvander@1915: return hndl; dvander@1915: } dvander@1915: dvander@1915: HandleError ReadHudSyncObj(Handle_t hndl, dvander@1915: IdentityToken_t *pOwner, dvander@1915: hud_syncobj_t **pObj) dvander@1915: { dvander@1915: HandleSecurity sec(pOwner, g_pCoreIdent); dvander@3872: return handlesys->ReadHandle(hndl, m_hHudSyncObj, &sec, (void **)pObj); dvander@1915: } dvander@1915: dvander@1915: unsigned int AutoSelectChannel(unsigned int client) dvander@1915: { dvander@1915: int last_channel; dvander@1915: player_chaninfo_t *player; dvander@1915: dvander@1915: player = &m_PlayerHuds[client]; dvander@1915: dvander@1915: last_channel = 0; dvander@1915: for (unsigned int i = 1; i < MAX_HUD_CHANNELS; i++) dvander@1915: { dvander@1915: if (player->chan_times[i] < player->chan_times[last_channel]) dvander@1915: { dvander@1915: last_channel = i; dvander@1915: } dvander@1915: } dvander@1915: dvander@1915: ManualSelectChannel(client, last_channel); dvander@1915: dvander@1915: return last_channel; dvander@1915: } dvander@1915: dvander@1915: int TryReuseLastChannel(unsigned int client, hud_syncobj_t *obj) dvander@1915: { dvander@1915: int last_channel; dvander@1915: player_chaninfo_t *player; dvander@1915: dvander@1915: player = &m_PlayerHuds[client]; dvander@1915: dvander@1915: /* First, see if we can re-use the previous channel. */ dvander@1915: last_channel = obj->player_channels[client]; dvander@1915: dvander@1915: if (player->chan_syncobjs[last_channel] == obj) dvander@1915: { dvander@1915: player->chan_times[last_channel] = *g_pUniversalTime; dvander@1915: return last_channel; dvander@1915: } dvander@1915: dvander@1915: return -1; dvander@1915: } dvander@1915: dvander@1915: int AutoSelectChannel(unsigned int client, hud_syncobj_t *obj) dvander@1915: { dvander@1915: int last_channel; dvander@1915: player_chaninfo_t *player; dvander@1915: dvander@1915: player = &m_PlayerHuds[client]; dvander@1915: dvander@1915: /* First, see if we can re-use the previous channel. */ dvander@1915: last_channel = obj->player_channels[client]; dvander@1915: dvander@1915: if (player->chan_syncobjs[last_channel] == obj) dvander@1915: { dvander@1915: player->chan_times[last_channel] = *g_pUniversalTime; dvander@1915: return last_channel; dvander@1915: } dvander@1915: dvander@1915: last_channel = 0; dvander@1915: for (unsigned int i = 1; i < MAX_HUD_CHANNELS; i++) dvander@1915: { dvander@1915: if (player->chan_times[i] < player->chan_times[last_channel]) dvander@1915: { dvander@1915: last_channel = i; dvander@1915: } dvander@1915: } dvander@1915: dvander@1915: obj->player_channels[client] = last_channel; dvander@1915: player->chan_syncobjs[last_channel] = obj; dvander@1915: player->chan_times[last_channel] = *g_pUniversalTime; dvander@1915: dvander@1915: return last_channel; dvander@1915: } dvander@1915: dvander@1915: int ManualSelectChannel(unsigned int client, int channel) dvander@1915: { dvander@1915: player_chaninfo_t *player; dvander@1915: dvander@1915: player = &m_PlayerHuds[client]; dvander@1915: player->chan_times[channel] = *g_pUniversalTime; dvander@1915: player->chan_syncobjs[channel] = NULL; dvander@1915: dvander@1915: return channel; dvander@1915: } dvander@1915: private: dvander@1915: HandleType_t m_hHudSyncObj; dvander@1915: player_chaninfo_t *m_PlayerHuds; dvander@1915: } s_HudMsgHelpers; dvander@1915: dvander@1915: hud_text_parms g_hud_params; dvander@1915: dvander@1915: static cell_t CreateHudSynchronizer(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: return s_HudMsgHelpers.CreateHudSyncObj(pContext->GetIdentity()); dvander@1915: } dvander@1915: dvander@1915: static cell_t SetHudTextParams(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: g_hud_params.x = sp_ctof(params[1]); dvander@1915: g_hud_params.y = sp_ctof(params[2]); dvander@1915: g_hud_params.holdTime = sp_ctof(params[3]); dvander@1915: g_hud_params.r1 = static_cast(params[4]); dvander@1915: g_hud_params.g1 = static_cast(params[5]); dvander@1915: g_hud_params.b1 = static_cast(params[6]); dvander@1915: g_hud_params.a1 = static_cast(params[7]); dvander@1915: g_hud_params.effect = params[8]; dvander@1915: g_hud_params.fxTime = sp_ctof(params[9]); dvander@1915: g_hud_params.fadeinTime = sp_ctof(params[10]); dvander@1915: g_hud_params.fadeoutTime = sp_ctof(params[11]); dvander@1915: g_hud_params.r2 = 255; dvander@1915: g_hud_params.g2 = 255; dvander@1915: g_hud_params.b2 = 250; dvander@1915: g_hud_params.a2 = 0; dvander@1915: dvander@1915: return 1; dvander@1915: } dvander@1915: dvander@1915: static cell_t SetHudTextParamsEx(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: cell_t *color1, *color2; dvander@1915: dvander@1915: pContext->LocalToPhysAddr(params[4], &color1); dvander@1915: pContext->LocalToPhysAddr(params[5], &color2); dvander@1915: dvander@1915: g_hud_params.x = sp_ctof(params[1]); dvander@1915: g_hud_params.y = sp_ctof(params[2]); dvander@1915: g_hud_params.holdTime = sp_ctof(params[3]); dvander@1915: g_hud_params.r1 = static_cast(color1[0]); dvander@1915: g_hud_params.g1 = static_cast(color1[1]); dvander@1915: g_hud_params.b1 = static_cast(color1[2]); dvander@1915: g_hud_params.a1 = static_cast(color1[3]); dvander@1915: g_hud_params.effect = params[6]; dvander@1915: g_hud_params.fxTime = sp_ctof(params[7]); dvander@1915: g_hud_params.fadeinTime = sp_ctof(params[8]); dvander@1915: g_hud_params.fadeoutTime = sp_ctof(params[9]); dvander@1915: g_hud_params.r2 = static_cast(color2[0]); dvander@1915: g_hud_params.g2 = static_cast(color2[1]); dvander@1915: g_hud_params.b2 = static_cast(color2[2]); dvander@1915: g_hud_params.a2 = static_cast(color2[3]); dvander@1915: dvander@1915: return 1; dvander@1915: } dvander@1915: dvander@1915: void UTIL_SendHudText(int client, const hud_text_parms &textparms, const char *pMessage) dvander@1915: { dvander@1915: cell_t players[1]; dvander@1915: dvander@1915: players[0] = client; dvander@1915: skamonkey@3855: #if SOURCE_ENGINE == SE_DOTA skamonkey@3855: CUserMsg_HudMsg *msg = (CUserMsg_HudMsg *)g_UserMsgs.StartProtobufMessage(g_HudMsgNum, players, 1, 0); skamonkey@3855: msg->set_channel(textparms.channel & 0xFF); skamonkey@3855: skamonkey@3855: msg->set_x(textparms.x); skamonkey@3855: msg->set_y(textparms.y); skamonkey@3855: skamonkey@3855: Color c1(textparms.r1, textparms.g1, textparms.b1, textparms.a1); skamonkey@3855: msg->set_color1(c1.GetRawColor()); skamonkey@3855: skamonkey@3855: Color c2(textparms.r2, textparms.g2, textparms.b2, textparms.a2); skamonkey@3855: msg->set_color2(c2.GetRawColor()); skamonkey@3855: skamonkey@3855: msg->set_effect(textparms.effect); skamonkey@3855: msg->set_fade_in_time(textparms.fadeinTime); skamonkey@3855: msg->set_fade_out_time(textparms.fadeoutTime); skamonkey@3855: msg->set_hold_time(textparms.holdTime); skamonkey@3855: msg->set_fx_time(textparms.fxTime); skamonkey@3855: msg->set_message(pMessage); skamonkey@3855: #elif SOURCE_ENGINE == SE_CSGO skamonkey@3743: CCSUsrMsg_HudMsg *msg = (CCSUsrMsg_HudMsg *)g_UserMsgs.StartProtobufMessage(g_HudMsgNum, players, 1, 0); skamonkey@3743: msg->set_channel(textparms.channel & 0xFF); skamonkey@3743: skamonkey@3743: CMsgVector2D *pos = msg->mutable_pos(); skamonkey@3743: pos->set_x(textparms.x); skamonkey@3743: pos->set_y(textparms.y); skamonkey@3743: skamonkey@3743: CMsgRGBA *color1 = msg->mutable_clr1(); skamonkey@3743: color1->set_r(textparms.r1); skamonkey@3743: color1->set_g(textparms.g1); skamonkey@3743: color1->set_b(textparms.b1); skamonkey@3743: color1->set_a(textparms.a1); skamonkey@3743: skamonkey@3743: CMsgRGBA *color2 = msg->mutable_clr2(); skamonkey@3743: color2->set_r(textparms.r2); skamonkey@3743: color2->set_g(textparms.g2); skamonkey@3743: color2->set_b(textparms.b2); skamonkey@3743: color2->set_a(textparms.a2); skamonkey@3743: skamonkey@3743: msg->set_effect(textparms.effect); skamonkey@3743: msg->set_fade_in_time(textparms.fadeinTime); skamonkey@3743: msg->set_fade_out_time(textparms.fadeoutTime); skamonkey@3743: msg->set_hold_time(textparms.holdTime); skamonkey@3743: msg->set_fx_time(textparms.fxTime); skamonkey@3743: msg->set_text(pMessage); skamonkey@3743: #else skamonkey@3743: bf_write *bf = g_UserMsgs.StartBitBufMessage(g_HudMsgNum, players, 1, 0); dvander@1915: bf->WriteByte(textparms.channel & 0xFF ); dvander@1915: bf->WriteFloat(textparms.x); dvander@1915: bf->WriteFloat(textparms.y); dvander@1915: bf->WriteByte(textparms.r1); dvander@1915: bf->WriteByte(textparms.g1); dvander@1915: bf->WriteByte(textparms.b1); dvander@1915: bf->WriteByte(textparms.a1); dvander@1915: bf->WriteByte(textparms.r2); dvander@1915: bf->WriteByte(textparms.g2); dvander@1915: bf->WriteByte(textparms.b2); dvander@1915: bf->WriteByte(textparms.a2); dvander@1915: bf->WriteByte(textparms.effect); dvander@1915: bf->WriteFloat(textparms.fadeinTime); dvander@1915: bf->WriteFloat(textparms.fadeoutTime); dvander@1915: bf->WriteFloat(textparms.holdTime); dvander@1915: bf->WriteFloat(textparms.fxTime); dvander@1915: bf->WriteString(pMessage); skamonkey@3743: #endif dvander@1915: g_UserMsgs.EndMessage(); dvander@1915: } dvander@1915: dvander@1915: static cell_t ShowSyncHudText(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: int client; dvander@1915: Handle_t err; dvander@1915: CPlayer *pPlayer; dvander@1915: hud_syncobj_t *obj; dvander@1915: char message_buffer[255-36]; dvander@1915: dvander@1915: if (!s_HudMsgHelpers.IsSupported()) dvander@1915: { dvander@1915: return -1; dvander@1915: } dvander@1915: dvander@1915: if ((err = s_HudMsgHelpers.ReadHudSyncObj(params[2], pContext->GetIdentity(), &obj)) != HandleError_None) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[2], err); dvander@1915: } dvander@1915: dvander@1915: client = params[1]; dvander@1915: if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Invalid client index %d", client); dvander@1915: } dvander@1915: else if (!pPlayer->IsInGame()) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Client %d is not in-game", client); dvander@1915: } dvander@1915: kyle@4060: g_SourceMod.SetGlobalTarget(client); dvander@1915: g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3); dvander@2175: if (pContext->GetLastNativeError() != SP_ERROR_NONE) dvander@1915: { dvander@1915: return 0; dvander@1915: } dvander@1915: dvander@1915: g_hud_params.channel = s_HudMsgHelpers.AutoSelectChannel(client, obj); dvander@1915: UTIL_SendHudText(client, g_hud_params, message_buffer); dvander@1915: dvander@1915: return 1; dvander@1915: } dvander@1915: dvander@1915: static cell_t ClearSyncHud(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: int client; dvander@1915: int channel; dvander@1915: Handle_t err; dvander@1915: CPlayer *pPlayer; dvander@1915: hud_syncobj_t *obj; dvander@1915: dvander@1915: if (!s_HudMsgHelpers.IsSupported()) dvander@1915: { dvander@1915: return -1; dvander@1915: } dvander@1915: dvander@1915: if ((err = s_HudMsgHelpers.ReadHudSyncObj(params[2], pContext->GetIdentity(), &obj)) != HandleError_None) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[2], err); dvander@1915: } dvander@1915: dvander@1915: client = params[1]; dvander@1915: if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Invalid client index %d", client); dvander@1915: } dvander@1915: else if (!pPlayer->IsInGame()) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Client %d is not in-game", client); dvander@1915: } dvander@1915: dvander@1915: if ((channel = s_HudMsgHelpers.TryReuseLastChannel(client, obj)) == -1) dvander@1915: { dvander@1915: return -1; dvander@1915: } dvander@1915: dvander@1915: g_hud_params.channel = channel; dvander@1915: UTIL_SendHudText(client, g_hud_params, ""); dvander@1915: dvander@1915: return g_hud_params.channel; dvander@1915: } dvander@1915: dvander@1915: static cell_t ShowHudText(IPluginContext *pContext, const cell_t *params) dvander@1915: { dvander@1915: int client; dvander@1915: CPlayer *pPlayer; dvander@1915: char message_buffer[255-36]; dvander@1915: dvander@1915: if (!s_HudMsgHelpers.IsSupported()) dvander@1915: { dvander@1915: return -1; dvander@1915: } dvander@1915: dvander@1915: client = params[1]; dvander@1915: if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Invalid client index %d", client); dvander@1915: } dvander@1915: else if (!pPlayer->IsInGame()) dvander@1915: { dvander@1915: return pContext->ThrowNativeError("Client %d is not in-game", client); dvander@1915: } dvander@1915: kyle@4060: g_SourceMod.SetGlobalTarget(client); dvander@1915: g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3); dvander@2175: if (pContext->GetLastNativeError() != SP_ERROR_NONE) dvander@1915: { dvander@1915: return 0; dvander@1915: } dvander@1915: dvander@1915: if (params[2] == -1) dvander@1915: { dvander@1915: g_hud_params.channel = s_HudMsgHelpers.AutoSelectChannel(client); dvander@1915: } dvander@1915: else dvander@1915: { dvander@1915: g_hud_params.channel = params[2] % MAX_HUD_CHANNELS; dvander@1915: s_HudMsgHelpers.ManualSelectChannel(client, g_hud_params.channel); dvander@1915: } dvander@1915: dvander@1915: UTIL_SendHudText(client, g_hud_params, message_buffer); dvander@1915: dvander@1915: return g_hud_params.channel; dvander@1915: } dvander@1915: dvander@1915: REGISTER_NATIVES(hudNatives) dvander@1915: { dvander@1915: {"ClearSyncHud", ClearSyncHud}, dvander@1915: {"CreateHudSynchronizer", CreateHudSynchronizer}, dvander@1915: {"SetHudTextParams", SetHudTextParams}, dvander@1915: {"SetHudTextParamsEx", SetHudTextParamsEx}, dvander@1915: {"ShowHudText", ShowHudText}, dvander@1915: {"ShowSyncHudText", ShowSyncHudText}, dvander@1915: {NULL, NULL}, dvander@1915: };